
Program Checkers;

Uses graph,crt,egadrv;  {egadrv is unit containing BGI driver}

(*                                                  *)
(*    A Proper Game of Draughts                     *)
(*                                                  *)
(*      Based on a program by Tim Hartnell          *)
(*    in Getting Acquainted With Your ZX81,         *)
(*       Creative Computing Press,1982.             *)
(*                                                  *)
(*   Board algorithm from Scientific Amer article.  *)
(*                                                  *)
(*     Requires EGA card                            *)
(*                                                  *)
(*    Peter Franchuk    3/2/90                      *)
(*       CIS ID [74146,225]                         *)
(*                                                  *)
(*                                                  *)
(*   Released to Public Domain                      *)
(*     for Personal use ONLY                        *)
(*                                                  *)

Const
  GDDrv='';   {for BGI driver if not registered}
  Direc:array[1..4] of integer=(-6,-7,6,7);

Type
  Pieces=(HuKing,HuMan,Blnk,CoMan,CoKing,OffBrd);
  Moverec=record
    row,col,value : integer;
    piece:pieces;
   end;

Var
  A : array [10..86] of Pieces;           {work array for pieces}
  Board : array [1..8,1..8] of byte;      {indices of squares--used for move}
  Use,Checker : array [1..12] of integer; {keep track of comp pieces location}
  jmpchk : array [24..72] of boolean;     {used to check for jumps}
  HuMove,Hufirst,Nmove,Njmp : boolean;
  HuName : string[10];
  PiecePtr : array [HuKing..CoKing] of pointer;  {images of pieces}
  FmSq,ToSq : Moverec;
  Total,Sum,Compcnt,KingCnt : word;
  ans : char;
  HuPiece,CoPiece : set of Pieces;

Procedure Set_Board;
  Var
    i,j,indx : integer;
    sqval : byte;
    strt : array [1..8] of byte;
  Begin
    HuPiece := [HuKing,HuMan];
    CoPiece := [CoKing,CoMan];
    for indx := 10 to 86 do A[indx] := offbrd;
    compcnt := 0;
    for indx := 69 to 72 do           {load up array with pieces}
     begin
      a[indx] := CoMan;
      inc(compcnt);checker[compcnt] := indx;
     end;
    for indx := 63 to 66 do
     begin
      a[indx] := Coman;
      inc(compcnt);checker[compcnt] := indx;
     end;
    for indx := 56 to 59 do
     begin
      a[indx] := Coman;
      inc(compcnt);checker[compcnt] := indx;
     end;
    for indx := 50 to 53 do a[indx] := blnk;
    for indx := 43 to 46 do a[indx] := blnk;
    for indx := 37 to 40 do a[indx] := HuMan;
    for indx := 30 to 33 do a[indx] := HuMan;
    for indx := 24 to 27 do a[indx] := Human;
    for i := 1 to 8 do for j := 1 to 8 do board[i,j] := 0;
    strt[1] := 72;strt[2] := 66;strt[3] := 59;
    strt[4] := 53;strt[5] := 46;
    strt[6] := 40;strt[7] := 33;strt[8] := 27;
    for i := 1 to 8 do
     begin
      if odd(i)
       then j := 2
       else j := 1;                    {load up board image}
      sqval := strt[i];
      while j<9 do
       begin
        board[i,j] := sqval;
        dec(sqval);inc(j,2);
       end;
     end;
    for i := 24 to 72 do jmpchk[i] := false;
    compcnt := 12;
    KingCnt := 0;
    total := 0;                        {initialize rest}
    sum := 0;
  End;

Procedure Draw_info;
  Var
    i,j : integer;
    ityp : pieces;
    Gd,Gm : integer;
    imsze : word;
  Begin
    SetFillStyle(SolidFill,LightRed);
    if Hufirst
     then j := 50
     else j := 300;
    i := 450;                          {draw and save checker images}
    FillEllipse(i,j,12,9);
    circle(i,j,9);circle(i,j,4);
    if Hufirst
     then j := 300
     else j := 50;
    i := 450;
    circle(i,j,12);circle(i,j,9);circle(i,j,4);
    imsze := Imagesize(1,1,33,25);
    for ityp := HuKing to CoKing do getmem(pieceptr[ityp],imsze);
    GetImage(434,38,466,62,pieceptr[CoMan]^);
    GetImage(1,1,33,25,pieceptr[blnk]^);
    GetImage(434,288,466,312,pieceptr[HuMan]^);
    j := 158;
    if Hufirst
     then i := 434
     else i := 334;
    GetImage(i,j,i+32,j+24,pieceptr[HuKing]^);
    if Hufirst
     then i := 334
     else i := 434;
    GetImage(i,j,i+32,j+24,pieceptr[CoKing]^);
    OuttextXY(330,50,'Computer');
    OutTextXY(330,300,HuName);
  End;

Procedure Who_first;
  Var
    ans,prmpt : char;
    Gd,Gm :integer;
    sx,sy :integer;
  Begin
    if registerBGIdriver(@EGAVGADriverProc)<0 then halt;
  {
     if not registering driver, comment out line above and be sure to have
      GdGrv constant defined to path of BGI driver at beginning of program.
  }
    Gd := EGA;Gm := EGAHi;
    Initgraph(gd,gm,gddrv);
    OutTextXY(362,130,'Checkers');
    SetFillStyle(SolidFill,LightRed);
    FillEllipse(350,170,12,9);
    OuttextXY(346,167,'K');
    circle(350,170,10);
    Circle(450,170,12);Circle(450,170,10);
    OuttextXY(446,167,'K');
    MoveTo(40,170);
    OutText('Your name, please : ');
    sx := GetX;sy := getY;
    ans := ' ';HuName := '';prmpt := '_';
    SetFillstyle(emptyfill,black);
    Repeat
     Bar(sx,sy,sx+80,sy+10);
     MoveTo(sx,sy);OutText(HuName+prmpt);
     Ans := readKey;
     if ans>#31
      then HuName := HuName+ans
      else if ans=#8 then HuName := copy(HuName,1,length(HuName)-1);
     HuName[1] := Upcase(HuName[1]);
    Until ans=#13;
    Bar(sx,sy,sx+80,sy+10);
    OutTextXY(sx,sy,HuName);
    OutTextXY(40,185,'Will you go first, '+HuName+' ? ');
    ans := ReadKey;
    HuFirst := ans in ['Y','y'];
    Draw_info;
    HuMove := Hufirst;
  End;

Procedure Draw_Board;
  Var cor,i,y :integer;
  Begin
    SetViewPort(36,65,334,311,ClipOn);
    SetFillstyle(Solidfill,Red);
    SetLinestyle(SolidLn,0,Thickwidth);
    SetColor(brown);
    bar(1,1,288,224);
    rectangle(1,1,288,224);
    cor := 28;y := 36;
    for i := 1 to 7 do
     begin
      line(y,0,y,224);                  {initial board}
      line(0,cor,288,cor);
      inc(cor,28);inc(y,36);
     end;
    for i := 1 to 3 do
     begin
      if odd(i)
       then cor := 2
       else cor :=1;
      while cor<9 do
       begin
        PutImage((cor-1)*36+2,(i-1)*28+2,pieceptr[CoMan]^,NormalPut);
        inc(cor,2);
       end;
     end;
    for i := 4 to 5 do
     begin
      if odd(i)
       then cor := 2
       else cor :=1;
      while cor<9 do
       begin
        PutImage((cor-1)*36+2,(i-1)*28+2,pieceptr[Blnk]^,NormalPut);
        inc(cor,2);
       end;
     end;
    for i := 6 to 8 do
     begin
      if odd(i)
       then cor := 2
       else cor :=1;
      while cor<9 do
       begin
        PutImage((cor-1)*36+2,(i-1)*28+2,pieceptr[HuMan]^,NormalPut);
        inc(cor,2);
       end;
     end;
    SetFillStyle(emptyFill,black);
    SetViewport(0,0,639,349,clipon);
    i := 50;y := 55; ans := '1';
    for cor := 1 to 8 do
     begin
      OutTextXY(i,y,ans);
      OutTextXY(i,295,ans);
      inc(i,36);ans := char( ord(ans) + 1);
     end;
    i := 24;y:=75; ans := 'A';
    for cor := 1 to 8 do
     begin
      OutTextXY(i,y,ans);
      inc(y,28);ans := char( ord(ans) +1);
     end;
    SetColor(white);
  End;

Procedure PlacePiece(v:integer;p:pieces);
  Var r,c:integer;
  Begin
     case v of
      72 : begin r := 1; c := 2 end;   27 : begin r := 8; c := 1 end;
      71 : begin r := 1; c := 4 end;   26 : begin r := 8; c := 3 end;
      70 : begin r := 1; c := 6 end;   25 : begin r := 8; c := 5 end;
      69 : begin r := 1; c := 8 end;   24 : begin r := 8; c := 7 end;
      66 : begin r := 2; c := 1 end;   59 : begin r := 3; c := 2 end;
      65 : begin r := 2; c := 3 end;   58 : begin r := 3; c := 4 end;
      64 : begin r := 2; c := 5 end;   57 : begin r := 3; c := 6 end;
      63 : begin r := 2; c := 7 end;   56 : begin r := 3; c := 8 end;
      53 : begin r := 4; c := 1 end;   46 : begin r := 5; c := 2 end;
      52 : begin r := 4; c := 3 end;   45 : begin r := 5; c := 4 end;
      51 : begin r := 4; c := 5 end;   44 : begin r := 5; c := 6 end;
      50 : begin r := 4; c := 7 end;   43 : begin r := 5; c := 8 end;
      40 : begin r := 6; c := 1 end;   33 : begin r := 7; c := 2 end;
      39 : begin r := 6; c := 3 end;   32 : begin r := 7; c := 4 end;
      38 : begin r := 6; c := 5 end;   31 : begin r := 7; c := 6 end;
      37 : begin r := 6; c := 7 end;   30 : begin r := 7; c := 8 end;
      end;
    SetViewPort(36,65,334,311,ClipOn);
    PutImage((c-1)*36+2,(r-1)*28+2,pieceptr[p]^,NormalPut);
    a[v] := p;
    SetViewPort(0,0,639,349,ClipOn);
  End;

Procedure Flash_piece(where:integer);
  Var ct,nb: integer;cpiece : pieces;
  Begin
   cpiece := a[where];
   if cpiece in HuPiece
    then nb := 2
    else nb := 4;
   for ct := 1 to nb do
    begin
     placepiece(where,blnk);
     delay(200);
     placepiece(where,cpiece);
     delay(100);
    end;
  End;

Procedure OpChkJmp(var Q:integer;z:integer);
  Var d : integer;
  Begin
    for d := 1 to 4 do                     {player piece can jump?}
    begin
     if (a[z]<>HuKing) and (d>2) then exit;
     if (a[z-direc[d]] in CoPiece) and (a[z-2*direc[d]]=blnk)
      then begin
       Q := direc[d];
       exit;
      end;
    end;
    Q := 0;
  End;


Procedure Player_move(var newmve : boolean);
  var
    rw,cl,j,i : integer;
    k1,k2 : char;
    good : boolean;

  Procedure Showmove;
    Begin
     flash_piece(fmsq.value);
     placePiece(fmsq.value,blnk);
     placePiece(tosq.value,fmsq.piece);
    End;

  Begin
    good := false;
    Setcolor(black);
    bar(335,90,630,270);
    Setcolor(white);
    OutTextXY(350,110,'Your move, '+HuName);
    if not newmve then
     begin
      j := 0;                        {can you really continue jump?}
      Opchkjmp(j,tosq.value);
      if j=0 then
       begin
        newmve := true;
        HuMove := false;
        exit;                        {you lied}
       end;
     end;
    if newmve
     then outtextxy(350,120,'Specifiy letter,number (or [ESC])')
     else outtextxy(355,120,'Continue your jump');
    repeat
     moveto(365,130);
     Outtext('Move from : ');
     bar(getx,130,550,140);
     if newmve
      then begin
        k1 := ' ';
        repeat
        k1 := upcase(readkey);
        until ((k1>='A') and (k1<='H')) or (k1=#27);
      end
      else k1 := char( tosq.row-1+ord('A') );
     bar(335,140,630,200);
     if k1=#27 then
      begin
       OutTextXY(340,150,'OK, GoodBye');
       delay(1500);
       closegraph;
       halt;
      end;
     Outtext(k1);
     rw := ord(k1)-ord('A') + 1;
     if newmve
      then begin
       k2 := ' ';
       repeat
       k2 := Readkey;
       until (k2>='1') and (k2<='8');
      end
      else k2 := char( tosq.col-1+ord('1') );
     outtext(k2);
     cl := ord(k2)-ord('1')+1;
     good := ( odd(rw) and (not odd(cl)) ) or ( (not odd(rw)) and odd(cl) );
     if not good then outtextXY(380,170,'You can''t do that');
     if good then
      begin
       FmSq.row := rw;FmSq.col := cl;
       Fmsq.value := Board[rw,cl];fmsq.piece := a[fmsq.value];
      end;
     if good then
      begin
       outtext(' to: ');
       k1 := ' ';
       repeat
       k1 := upcase(readkey);
       until (k1>='A') and (k1<='H');
       Outtext(k1);
       rw := ord(k1)-ord('A') + 1;
       k2 := ' ';
       repeat
       k2 := Readkey;
       until (k2>='1') and (k2<='8');
       outtext(k2);
       cl := ord(k2)-ord('1')+1;
      end;
     good := (odd(rw) and (not odd(cl))) or ((not odd(rw)) and odd(cl));

     if not good then outtextXY(380,170,'You can''t do that');

     if good then
      begin
       toSq.row := rw;toSq.col := cl;
       tosq.value := Board[rw,cl];tosq.piece := a[tosq.value];
      end;

     if good and jmpchk[fmsq.value] and (abs(fmsq.value-tosq.value)<=7)
      then begin
       good := false;
       OutTextXY(340,160,'You must make the jump.');
      end;

     if good and (fmsq.piece=Human) then begin
      good := (tosq.value-fmsq.value) > 0;
      if not good then outtextXY(380,170,'Only Kings move backwards');
      end;

     if good then begin
       good:=((fmsq.piece=human) or (fmsq.piece=huKing)) and (tosq.piece=blnk);
       if not good then
        begin outtextXY(340,180,'You must move YOUR man');
        outtextXY(340,190,' to an adjacent free space');end;
       end;

     if good then begin
       good := ( (abs(fmsq.value-tosq.value) mod 6)=0 ) or
        ( (abs(fmsq.value-tosq.value) mod 7)=0 );
       if not good then outtextXY(380,170,'Must move on the diagonal');
       end;

     if good and (abs(fmsq.value-tosq.value)>7) then begin
       good := a[ (fmsq.value+tosq.value) div 2 ] in CoPiece;
       if not good then outtextXY(380,170,'You can only jump an opponent');
       end;

     if (not newmve) and (not good) then tosq := fmsq;
    Until good;

    rw := 0;
    if abs(fmsq.value-tosq.value)>7 then     {is it a jump?}
     begin
      outtextXY(350,150,'Multiple jump?');
      ans := readkey;
      HuMove := ans in ['Y','y'];
      rw := (fmsq.value+tosq.value) div 2;
      a[rw] := blnk;
      j := 1;                                {remove the comp piece}
      while checker[j]<>rw do inc(j);
      if a[checker[j]]=CoKing then dec(KingCnt);
      if j<compcnt then
       for i := j to compcnt-1 do checker[i] := checker[i+1];
      dec(compcnt);
      inc(total);
      if (tosq.value>68) and (fmsq.piece=HuMan)
       then begin
        newmve := true;
        HuMove := false;
       end                                   {reaching Kings row ends move}
       else Newmve := false;
      Njmp := false;
     end
     else
      begin
       HuMove := false;
       Newmve := true;
       Njmp := true;
      end;
    ShowMove;
    if rw>0 then  placePiece(rw,blnk);
  End;

Procedure disp_status;
  Var
    cnt:string;
  Begin
    bar(438,38,472,62);
    SetTextStyle(Defaultfont,horizdir,2);
    str(sum,cnt);
    outtextXY(440,45,cnt);
    bar(438,288,472,312);                     {show the score}
    str(total,cnt);
    outtextXY(440,293,cnt);
    SetTextStyle(defaultfont,horizdir,1);
    if (sum=12) or (total=12) then
     begin
      bar(335,90,630,270);                    {game over}
      If sum=12 then
        begin
        SetTextstyle(defaultfont,horizdir,2);
        OutTextXY(350,100,'I WIN!');
        end;
      if total=12 then OutTextXY(360,100,'You win.');
      ans := readkey;
      Closegraph;
      halt;
     end;
  End;


Procedure Comp_move;
  Var
    Q,z,i,d,j : integer;

  Procedure CrownKings;
    Var z : integer;
    Begin
      for z := 69 to 72 do
       if a[z]=Human then
        begin
         a[z] := HuKing;
         placePiece(z,Huking);
        end;
      for z := 24 to 27 do
       if a[z]=Coman then
        begin
         a[z] := CoKing;
         inc(KingCnt);
         placePiece(z,CoKing);
        end;
    End;


  Procedure ChkHuman;
    Var
      i,Q :integer;
    Begin
      Q := 0;
      for i := 24 to 72 do
       if jmpchk[i] and (a[i] in HuPiece) then begin
        OpChkJmp(Q,i);
        if Q<>0 then begin
         OuttextXY(350,140,'You missed a jump');
         flash_piece(i);
         flash_piece(i);
         OuttextXY(355,150,'Hit any key to continue');
         ans := readkey;
         placepiece(i,blnk);
         inc(sum);
         disp_status;
         exit;
        end;
       end;
    End;

  Procedure ChkJmp(var Q:integer);
    Var d : integer;
    Begin
      for d := 1 to 4 do              {comp piece [z] has a jump?}
      begin
       if (a[z]<>CoKing) and (d>2) then exit;
       if (a[z+direc[d]] in HuPiece) and (a[z+2*direc[d]]=blnk)
         then begin
          Q := direc[d];
          exit;
       end;
      end;
      Q := 0;
    End;

  Procedure Do_Jmp;
    Var j : integer;
    Begin
      flash_piece(z);
      placePiece(z+2*Q,a[z]);
      j := 1;
      While checker[j]<>z do inc(j);
      placePiece(z,blnk);
      checker[j] := z+2*Q;
      inc(Sum);
      placePiece(z+Q,blnk);
    End;

  Procedure Do_Move(oldsq,amnt:integer);
    Var i,nwsq : integer;
    Begin
      nwsq := oldsq + amnt;
      flash_piece(oldsq);
      placepiece(nwsq,a[oldsq]);
      i := 1;
      while checker[i]<>oldsq do inc(i);
      checker[i] := nwsq;
      placepiece(oldsq,blnk);
    End;

  Procedure Random_move;
    Var
      z,d : integer;
      n,j : word;
      gmove : set of pieces;
    Begin
      n := compcnt;
      use := checker;
      while n>0 do
       begin
        j := 1 + Random(n);             {see if we can slide inbetween}
        if j>n then j := n;
        if j<1 then j := 1;
        if (n>2) and (j=n) then dec(j);
        z := use[j];
        for d := 1 to 2 do
         begin
          if (a[z+direc[d]]=blnk) and (not (a[z+2*direc[d]] in HuPiece))
            and (a[z-13]<>Blnk) and (a[z-1+2*(d mod 2)]<>Blnk)
           then begin
            Do_Move(z,direc[d]);
            exit;
           end;
          if (a[z]=CoKing) and (z<55) and (a[z-direc[d]]=blnk)
            and (not (a[z-2*direc[d]] in HuPiece))
            and (a[z+13]<>Blnk) and (a[z-1+2*(d div 2)]<>Blnk)
           then begin
            Do_Move(z,-direc[d]);
            exit;
           end;
         end;
        use[j] := use[n];
        dec(n);
       end;
      n := compcnt;
      use := checker;
      while n>0 do
       begin
        j := 1 + Random(n);
        if j<1 then j := 1;
        if j>n then j := n;
        if (n>2) and (j=n) then dec(j);
        z := use[j];
        for d := 2 downto 1 do                 {otherwize not into a jump}
         begin
          if (a[z+direc[d]]=blnk)
           and ((a[z]=CoMan) or (KingCnt=compcnt))
           and (not (a[z+2*direc[d]] in HuPiece))
           and (not (a[z+13] in HuPiece)) then
           begin
            Do_Move(z,direc[d]);
            exit;
           end;
          if (kingcnt>compcnt-2) and (a[z]=CoKing) and (z<55)
                     and (a[z-direc[d]]=blnk) and
           (not (a[z-2*direc[d]] in HuPiece)) and (not (a[z-13] in HuPiece))
           then begin
            Do_Move(z,-direc[d]);
            exit;
           end;
         end;
        use[j] := use[n];
        dec(n);
       end;
      n := compcnt;
      use := checker;
      while n>0 do
       begin
        j := 1 + Random(n);
        if j>n then j := n;
        if j<1 then j := 1;
        if (n>2) and (j=n) then dec(j);
        z := use[j];
        for d := 1 to 2 do                 {otherwize just move}
         begin
          if (a[z+direc[d]]=blnk) then
           begin
            Do_Move(z,direc[d]);
            exit;
           end;
          if (a[z]=CoKing) and (z<55) and (a[z-direc[d]]=blnk)
           then begin
            Do_Move(z,-direc[d]);
            exit;
           end;
         end;
        use[j] := use[n];
        dec(n);
       end;
      OuttextXY(350,100,'I concede');
      ans := readkey;                      {or give up}
      closegraph;                          {game over}
      halt;
    End;

  Begin
    CrownKings;
    bar(335,90,630,270);
    OutTextXY(350,110,'MY Move..');
    delay(1000);                     {gives player a chance to look at screen}

{    outtextXY(355,120,'Hit any key');
    ans := readkey; }        {alternate form for beginning computer move}


    if Njmp then ChkHuman;            {check for missed jumps}
    i := 1;Q :=0;
    while (Q=0) and (i<=compcnt) do
     begin
      z := checker[i];
      ChkJmp(Q);inc(i);               {can the computer jump?}
     end;
    if Q<>0
     then while Q<>0 do begin
      Do_Jmp;
      z := z+2*Q;
      Q := 0;                         {yes, do it}
      ChkJmp(Q);                      {and check for more}
     end
     else begin
      i := 1;
      while (Q=0) and (i<=compcnt) do
       begin
        d := 1;                         {check for blocking jump}
        z := checker[i];
        repeat
          j := direc[d];
           if (a[z+j] in HuPiece) and (a[z-j]=blnk) then
            begin
             if (a[z-2*j] in CoPiece)
              then Q := z-2*j
              else if (a[z+13] in CoPiece) then
               begin
                Q := z+13;
                j := direc[ (d mod 2) +1 ];
               end;
             end;
          if (Q=0) and (a[z+2*j]=CoKing) then
           begin
            j := -j;
            if (a[z+j] in HuPiece) and (a[z-j]=blnk)
              then Q := z-2*j;
           end;
          inc(d);
        until (Q<>0) or (d>2);
        inc(i);
       end;
      if Q<>0
       then Do_Move(Q,j)
       else begin
        i := 1;
        while (Q=0) and (i<=compcnt) do
         begin
          z := checker[i];
          if a[z]=CoKing then
           begin
            d := 3;
            repeat                       {can you use the king?}
             j := direc[d];
             if (a[z+j]=blnk) and (a[z+2*j]=HuMan)
              and (a[z+3*j]=blnk) then
               if (not (a[z-1+2*((d-2) div 2)] in Hupiece))
                then Q := z;
             inc(d);
            until (Q<>0) or (d>4);
           end;
          inc(i);
         end;
        if Q<>0
         then Do_Move(Q,j)
         else Random_move;                {else random move}
        end;
      end;
    for i := 24 to 72 do
      if not (a[i] in HuPiece)
       then jmpchk[i] := false
       else begin
        Q := 0;
        OpChkJmp(Q,i);                {set check array for human jumps}
        jmpchk[i] := Q<>0;
       end;
    Disp_status;
    HuMove := true;
    CrownKings;
    Nmove := true;
  End;

Procedure Play_game;
  Var
    forever : boolean;
  Begin
    forever := false;
    repeat
     if Humove then Player_move(Nmove);
     disp_status;
     if not Humove then Comp_move;
    until forever;
  End;


BEGIN
  Set_board;
  Who_first;
  Draw_board;
  Nmove := true;
  Randomize;
  Play_game;
END.
