#name "DewSoft Stones v1.0"
/*
   This file is copyright 1997 by Jeremy Dewey.
*/

#include "usglib.h"
#include "lib\tabline.c"
#include "lib\sprite.c"
#include "lib\sprite_x.c"
#include "lib\rand.c"
#include "lib\disp_dec.c"

#define true 1
#define false 0

#define P_BLOCK 25
#define P_HUMAN -10
#define P_CPU 10

/* Sprites - actual data at end of file */
extern char icon_h[], icon_hi[], icon_c[], icon_ci[], icon_sq[];
extern char wait_L1[], wait_0[], icon_0[], icon_b[], square[];
/* Various pointers */
char *brd, *newbrd, *p_wait, i_wait;
/* Human's current cursor position */
int cx, cy;
/* Current scores */
char numci, numc, numhi, numh, nummt;
char onumci, onumc, onumhi, onumh;
extern char* _frstmem(0x8b27);

main() {
   char i;
   i_wait = 0;
   cx = 8;
   cy = 5;

   // To allocate board space, this game requires 352 bytes free
   brd = _frstmem;
   newbrd = _frstmem + 176;

   while (true) {
      clrscr;
      //graphpage(); - Usgard now does this automatically
      tabline("Stones v1.0");
      /* Print my name */
      my_name();

      /* Draw message window */
      vline(9,63,92);

      /* Populate message window */
      setxy_m(100,8);
      puts_m("Human");
      for (i=9;i<15;i++) hline(93,127,i);
      setxy_m(103,15);
      puts_m("0 ");
      sprite(icon_h,_menu_x, 16,5,5);
      setxy_m(103,21);
      puts_m("0 ");
      sprite(icon_hi,_menu_x, 22,5,5);
      setxy_m(103,27);
      puts_m("0 Total");

      setxy_m(95,36);
      puts_m("CPU stats");
      for (i=36;i<43;i++) hline(93,127,i);
      setxy_m(103,43);
      puts_m("0 ");
      sprite(icon_c,_menu_x, 44,5,5);
      setxy_m(103,49);
      puts_m("0 ");
      sprite(icon_ci,_menu_x, 50,5,5);
      setxy_m(103,55);
      puts_m("0 Total");

      // Check for amount of free memory
      if (free_mem() < 352) {
         setxy_m(4,10);
         puts_m("Stones requires 352 bytes");
         setxy_m(20,16);
         puts_m("of free memory");
         getkey();
         _exit();
      }
      init_brd();
      draw_brd();
      // Change the text at the top of the screen
      play();
      my_name();
      game_over();
      if (getkey() == K_EXIT) _exit();
      // Change the text back
      game_over();
      my_name();
   }
}

free_mem() {
   asm("   call FREEMEM\n");
}

my_name() {
   setxy_m(70, 1);
   puts_m("by Jeremy Dewey");
}

game_over() {
   setxy_m(50,1);
   puts_m("Game Over - press a key");
}

/* Run the game until there are no more empty spaces with cookie jars */

play() {
   nummt=1;
   while (nummt) {
      if (human()) return; // If human() returns 1, exit game early
      draw_brd();
      if (nummt) {
         cpu();
         draw_brd();
      }
   }
}

/* Wait cursor */

waitcycl() {
   p_wait = wait_L1 + i_wait * 10;
   sprite(p_wait  ,48,2,8,5);
   sprite(p_wait+5,56,2,8,5);
   if (++i_wait >=4) i_wait=0;
}

waitoff() {
   sprite(wait_0, 48,2,8,5);
   sprite(wait_0, 56,2,8,5);
}

/* Initialize the board (with blocks surrounding the visible area */

init_brd() {
   int x,y,b;

   for(y=0;y<11;y++) {
      /*waitcycl();*/
      for(x=0;x<16;x++) {
         b = bindex(x,y);
         if (!x || x==15 || !y || y==10 || rand16() < 5000) {
            brd[b] = P_BLOCK;
         }
         else brd[b] = 0;
      }
   }
}

/* Draw the board */

draw_brd() {
   int xx,yy,x,y,b;
   char* spr;

   yy = 10;
   onumci = numci;
   onumc = numc;
   onumhi = numhi;
   onumh = numh;

   numci = numc = numhi = numh = nummt = 0;
   for(y=1;y<10;y++) {
      waitcycl();
      xx = 1;
      for(x=1;x<15;x++) {
         b = bindex(x,y);
         if (brd[b]==P_BLOCK) spr = icon_b;
         else if (brd[b]==P_HUMAN) { spr = icon_h; numh++;}
         else if (brd[b]==P_CPU) { spr = icon_c; numc++;}
         else if (brd[b]>127) { spr = icon_hi; numhi++;}
         else if (!brd[b]) { spr = icon_0; nummt++;}
         else { spr = icon_ci; numci++;}
         sprite(spr,xx,yy,5,5);
         xx+=6;
      }
      yy+=6;
   }
   waitoff();
   draw_scr(15,onumh,numh);
   draw_scr(21,onumhi,numhi);
   draw_scr(27, onumh+onumhi,numh+numhi);

   draw_scr(43,onumc,numc);
   draw_scr(49,onumci,numci);
   draw_scr(55,onumc+onumci,numc+numci);
}

draw_scr(y,os,ns) char y,os,ns; {
   draw_sc2(y,os); // erase old score
   draw_sc2(y,ns); // draw new score
}

draw_sc2(y,scr) char y,scr; {
   setxy_m(95, y);
   disp_char(scr);
}

/* Let human choose their move */

human() {
   int c,done,xx,yy;
   done = 0;

   do {
      xx = cx*6-6;
      yy = cy*6+3;
      sprite_x(square,xx,yy,7,7);
      c = getkey();
      sprite_x(square,xx,yy,7,7);
      if (c == K_DOWN) {
         if (cy < 9) cy++;
         else cy = 1;
      } else if (c == K_UP) {
         if (cy > 1) cy--;
         else cy = 9;
      } else if (c == K_LEFT) {
         if (cx > 1) cx--;
         else cx = 14;
      } else if (c == K_RIGHT) {
         if (cx < 14) cx++;
         else cx = 1;
      } else if (c == K_EXIT)
         return 1;
      else if (c == K_SECOND) {
         if (islegal(cx,cy,P_HUMAN)) {
            done = 1;
            execute(cx,cy,P_HUMAN,brd);
         }
      }
   } while (!done);
   draw_brd();
   return 0;
}

/* Find the best legal move */

cpu() {
   int val, x,y;
   int bestVal, bestX, bestY;

   bestVal=0;
   for (y=1;y<10;y++)
      for (x=1;x<15;x++) {
         if (islegal(x,y,P_CPU)) {
            if (checkkey() == K_EXIT) _exit();
            copy_brd();
            execute(x,y,P_CPU,newbrd);
            val = eval_brd();
            if (val>bestVal) { bestX=x; bestY=y; bestVal=val; }
            waitcycl();
         }
      }
   execute(bestX,bestY,P_CPU,brd);
   waitoff();
}

/* Helper functions */
sign(a) char a; {
   if (a>127) return -1;
   return 1;
}

bindex(x,y) int x,y; {
   return (y<<4)+x;
}

abs(a) char a; {
   if (a>127) return -a;
   return a;
}

islegal(x,y,p) int x,y,p; {
   char b;
   b = brd[bindex(x,y)];
   if (!b || (sign(b)==sign(p) && b!=p && b!=P_BLOCK))
      return true;
   return false;
}

/* Modify the board with a CPU or human move */

execute(x,y,p,b) int x,y,p; char* b; {
   int i,j,xx,yy,bb;

   for(i=0;i<3;i++)
      for(j=0;j<3;j++) {
         xx = x + j - 1;
         yy = y + i - 1;
         bb = bindex(xx,yy);
         if (j==1 && i==1) b[bb] = p;
         else if (abs(b[bb]) < 10) b[bb]+=sign(p);
      }
}

can_cpu_place(bi) int bi; {
   char spot;
   spot = newbrd[bi];
   if (!spot || (sign(spot)==1 && spot<10)) return true;
   return false;
}

/* This function is never actually called
can_human_place(bi) int bi; {
   char spot;
   spot = newbrd[bi];
   if (!spot || (sign(spot)==-1 && abs(spot)<10)) return true;
   return false;
}
*/

eval_brd() {
   int x,y,nbi,i,j,bi;
   int score, factor, vuln;

   score = 5000;
   for (y=1;y<10;y++) {
      for (x=1;x<15;x++) {
         bi = bindex(x,y);
         // Score each taken block
         if (!newbrd[bi]) continue; // No need to score an empty space
         if (newbrd[bi]==P_BLOCK) continue; // It's a block!
         if (newbrd[bi]==P_CPU) { score+=10; continue; }
         if (newbrd[bi]==P_HUMAN) { score-=10; continue; }

         // Score each influenced block

/* This code takes to long to run...

         vuln=0; // vulnerability
         if (can_cpu_place(bi)) factor = 1;
         else factor = -1;

         for (j=0;j<3;j++)
            for (i=0;i<3;i++) {
               if (i==1 && j==1) continue;
               nbi = bindex(x+i-1,y+j-1);
               if (factor==1 && can_human_place(nbi) ||
                   factor==-1 && can_cpu_place(nbi)) vuln++;
            }
         // Now vuln becomes the strength of the position
         vuln = factor * newbrd[bi] - vuln;
         if (vuln > 0) score+=10*factor;
         else score+=8*factor;

   It can be approximated thusly...
*/
         if (can_cpu_place(bi)) {
            if (newbrd[bi] > 1) score+=10;
            else score+=8;
         } else { /* human can place */
            if (newbrd[bi] < -1) score-=10;
            else score-=8;
         }

      }
   }
   return score;
}

copy_brd() {
// make a quick copy of the board for modification
#asm
   ld  hl,(brd)      ;source
   ld  de,(newbrd)   ;dest
   ld  bc,176        ;board size
   ldir
#endasm
}

checkkey() {
#asm
   call GET_KEY
   ld hl,0
   ld l,a
#endasm
}

/* Sprite data */
#asm
icon_h:
.db %00100000
.db %01110000
.db %11111000
.db %01110000
.db %00100000
icon_hi:
.db %00100000
.db %01010000
.db %10001000
.db %01010000
.db %00100000
icon_c:
.db %01110000
.db %11111000
.db %11111000
.db %11111000
.db %01110000
icon_ci:
.db %01110000
.db %10001000
.db %10001000
.db %10001000
.db %01110000
icon_b:
.db %11111000
.db %10101000
.db %11011000
.db %10101000
.db %11111000
icon_0:
.db %00000000
.db %00000000
.db %00000000
.db %00000000
.db %00000000
square:
.db %11111110
.db %10000010
.db %10000010
.db %10000010
.db %10000010
.db %10000010
.db %11111110
wait_0:
.db %00000000
.db %00000000
.db %00000000
.db %00000000
.db %00000000
wait_L1:
.db %11111111
.db %10011001
.db %10110011
.db %11100110
.db %11111111
wait_1R:
.db %11111111
.db %10011001
.db %00110011
.db %01100111
.db %11111111
wait_2L:
.db %11111111
.db %10110011
.db %11100110
.db %11001100
.db %11111111
wait_2R:
.db %11111111
.db %00110011
.db %01100111
.db %11001101
.db %11111111
wait_3L:
.db %11111111
.db %11100110
.db %11001100
.db %10011001
.db %11111111
wait_3R:
.db %11111111
.db %01100111
.db %11001101
.db %10011001
.db %11111111
wait_4L:
.db %11111111
.db %11001100
.db %10011001
.db %10110011
.db %11111111
wait_4R:
.db %11111111
.db %11001101
.db %10011001
.db %00110011
.db %11111111
#endasm


