/* kleines Spielchen 31.1.93 by gum */

#include <exec/types.h>
#include <stdlib.h>
#include <limits.h>
#include <time.h>                   /* für init rand() */
#include <string.h>
#include <intuition/intuition.h>
#include <proto/intuition.h>
#include <proto/graphics.h>
#include <proto/dos.h>
#include <proto/exec.h>
#include <assert.h>
#include <stdio.h>                  /* für stdout, printf() */

#define SPIELER      1
#define COMPI        2
#define ON           1
#define OFF          2
#define ONE_WAY      2
#define LAST_FIELD   1
#define EV_QUIT      -1
#define EV_GAME_OVER -2
#define EV_NEW_GAME  -3
#define RND(x)       (rand()%(x))
#define ABS(x)       ((x>=0)?(x):-(x))
#define TEXTLEN(x)   TextLength(rp,x,strlen(x))
#define PLY1_NAME    "Player 1"
#define PLY2_NAME    "Computer"
#define PKT_NAME     "0000"
#define W_STD_WIDTH  168
#define W_STD_HEIGHT 134
#define NDEBUG1      1
#define NDEBUG2      1
#define FINAL        1

int rand(void);
void srand(unsigned int); /* wo sind die bloß ? */

#ifndef FINAL
void main(int,char **);
#else
void _main(char *arg);
#endif
void macheZug(int);
int MMcleanup(int);
void init1(void);
void init2(void);
int CompiZug(void);
int SimCompi(int,int,int);
int SimSpieler(int,int,int);
void print2(int,int,int);
void print3(int,int,int);
void highlight(int,int);
int getEvent(void);
void setBenutzt(int);
int FrageEnde(void);

char *whatis = "\n\0$VER: MiniMax 0.8 (31.01.93)";

struct Library *IntuitionBase=0L;
struct Library *GfxBase=0L;

struct Gadget feld[64]; /* 8*8 */
struct NewWindow g_win = {
   50,50,W_STD_WIDTH,W_STD_HEIGHT,1,2,
   GADGETUP|CLOSEWINDOW, SMART_REFRESH|WINDOWCLOSE|WINDOWDRAG,
   feld,
   NULL,
   "MiniMax",
   NULL,
   NULL,
   0,0,0,0,
   WBENCHSCREEN
};
struct Window *MMwin=0L;
struct RastPort *rp;

struct Gadget vorlage = {
   0L,0,0,17,9,GADGHNONE,GADGIMMEDIATE|RELVERIFY,BOOLGADGET,0L,0L,0L,0L,0L,0,0L
};

int maxit=3,zeile,spalte,MiniMax[8][8];
int CompiPkte,SpielerPkte,endep;
#ifndef NDEBUG2
int anz = 63;
#endif
int FONT_HEIGHT = 8;     /* MMwin->RPort->Font->tf_YSize */
int PKT_X1      = 30;    /* MIN_X + (TEXTLEN(PLY1_NAME)-TEXTLEN(PKT_NAME))/2 */
int PKT_X2      = 110;   /* MAX_X - (TEXTLEN(PLY2_NAME)+TEXTLEN(PKT_NAME))/2 */
int PKT_Y       = 28;    /* PLAYER_Y+FONT_HEIGHT */
int SPALTEN     = 18;    /* 2 + TEXTLEN("00") */
int ZEILEN      = 10;    /* 2 + FONT_HEIGHT*/
int MIN_X       = 12;    /* 10 + MMwin->BorderLeft */
int MAX_X       = 12+8*18; /* MIN_X+8*SPALTEN; */
int MIN_Y       = 40;    /* PKT_Y + 12 */
int MAX_Y       = 40+8*10; /* MIN_Y+8*ZEILEN; */
int PLAYER_X1   = 12;    /* MIN_X */
int PLAYER_X2   = 94;    /* MAX_X-TEXTLEN(PLY2_NAME) */
int PLAYER_Y    = 20;    /* MMwin->BorderTop+1+FONT_HEIGHT */
int WIN_WIDTH   = 168;   /* MAX_X+10+MMwin->BorderRight */
int WIN_HEIGHT  = 134;   /* MAX_Y+10+MMwin->BorderBottom */

#ifndef FINAL
void main(int argc,char *argv[])
#else
void _main(char *arg)
#endif
{
   srand((unsigned)time(NULL));
   init1();
   do {
      init2();
      spalte = RND(8);
      highlight(SPIELER,ON);
      while (!endep && (zeile = getEvent()) >= 0) {
         macheZug(SPIELER);
         highlight(SPIELER,OFF);
         
         highlight(COMPI,ON);
         spalte = CompiZug();
         macheZug(COMPI);
         highlight(COMPI,OFF);
         if (endep != LAST_FIELD) highlight(SPIELER,ON);
      }
      /* jetzt ist zeile negativ */
      if (endep) zeile = FrageEnde();
   } while (zeile != EV_QUIT);
   /*MMcleanup(0); wird schon in init1() festgelegt */
}

void macheZug(int Ply)
{
   register int *z;

   assert(spalte >= 0 && spalte < 8);
   assert(zeile >= 0 && zeile < 8);   
   z = ((Ply == SPIELER)?&SpielerPkte:&CompiPkte);
   *z += MiniMax[zeile][spalte];
   MiniMax[zeile][spalte] = ~MiniMax[zeile][spalte];
   setBenutzt(Ply);
   print3(*z,((Ply == SPIELER)?PKT_X1:PKT_X2),PKT_Y);
}

int MMcleanup(int err)
{
   struct Message *msg;
   
   if (MMwin) {
      while (msg = GetMsg(MMwin->UserPort)) ReplyMsg(msg);
      CloseWindow(MMwin);
   }
   if (GfxBase) CloseLibrary(GfxBase);
   if (IntuitionBase) CloseLibrary(IntuitionBase);
   return(err);
}

void init1(void)
{
   int i,x,y;
   struct Gadget *g;
   
   onexit(MMcleanup);
   if (!(IntuitionBase = OpenLibrary("intuition.library",33L))) exit(10);
   if (!(GfxBase = OpenLibrary("graphics.library",33L))) exit(10);
   x = MIN_X;
   y = MIN_Y;
   repmem((char *)feld,(char *)&vorlage,sizeof(struct Gadget),64);
   for(g=feld;g<feld+64;g++) {
      g->NextGadget = g+1;
      g->LeftEdge = x+1;
      g->TopEdge = y+1;
      g->GadgetID = g-feld;
      x += SPALTEN;
      if (x >= MAX_X) {
         x = MIN_X;
         y += ZEILEN;
      }
   }
   feld[63].NextGadget = 0L;
   if (!(MMwin = OpenWindow(&g_win))) exit(5);
   rp = MMwin->RPort;
   if (rp->Font->tf_YSize != FONT_HEIGHT) {
      /* Da hat einer an den Default-Fonts 'rumgespielt */
      FONT_HEIGHT = MMwin->RPort->Font->tf_YSize;
      SPALTEN = 2 + TEXTLEN("00");
      ZEILEN = 2 + FONT_HEIGHT;
      MIN_X = 10 + MMwin->BorderLeft;
      MAX_X = MIN_X+8*SPALTEN;
      PLAYER_X1 = MIN_X;
      PLAYER_X2 = MAX_X-TEXTLEN(PLY2_NAME);
      PLAYER_Y = MMwin->BorderTop+1+FONT_HEIGHT;
      PKT_X1 = MIN_X + (TEXTLEN(PLY1_NAME)-TEXTLEN(PKT_NAME))/2;
      PKT_X2 = MAX_X - (TEXTLEN(PLY2_NAME)+TEXTLEN(PKT_NAME))/2;
      PKT_Y  = PLAYER_Y+FONT_HEIGHT;
      MIN_Y = PKT_Y + 12;
      MAX_Y = MIN_Y+8*ZEILEN;
      WIN_WIDTH = MAX_X+10+MMwin->BorderRight;
      WIN_HEIGHT = MAX_Y+10+MMwin->BorderBottom;
      CloseWindow(MMwin);
      /* Die Brechstange im Haus ersetzt fehlende Dokumentation  :( */
      g_win.Width = WIN_WIDTH;
      g_win.Height = WIN_HEIGHT;
      for(g=feld,x=MIN_X,y=MIN_Y;g<feld+64;g++) {
         g->LeftEdge = x+1;
         g->TopEdge = y+1;
         x += SPALTEN;
         if (x >= MAX_X) {
            x = MIN_X;
            y += ZEILEN;
         }
      }
      if (!(MMwin = OpenWindow(&g_win))) exit(5);
      rp = MMwin->RPort;
   }
   SetAPen(rp,1);
   for (i=MIN_X;i<=MAX_X;i+=SPALTEN) {
      Move(rp,i,MIN_Y);
      Draw(rp,i,MAX_Y);
   }
   for (i=MIN_Y;i<=MAX_Y;i+=ZEILEN) {
      Move(rp,MIN_X,i);
      Draw(rp,MAX_X,i);
   }
   Move(rp,PLAYER_X1,PLAYER_Y);
   Text(rp,PLY1_NAME,strlen(PLY1_NAME));
   Move(rp,PLAYER_X2,PLAYER_Y);
   Text(rp,PLY2_NAME,strlen(PLY2_NAME));
}

void init2(void)
{
   int x,y,n;
   
   Move(rp,PKT_X1,PKT_Y);
   Text(rp,"0000",4);
   Move(rp,PKT_X2,PKT_Y);
   Text(rp,"0000",4);
   for (y=0;y<8;y++)
      for (x=0;x<8;x++) {
         n = MiniMax[y][x] = RND(100);
         print2(n,x*SPALTEN+MIN_X+1,y*ZEILEN+MIN_Y+FONT_HEIGHT);
      }
   endep = 0;
}

int SimCompi(int spl,int zl,int it)
{
   int best = INT_MIN+LAST_FIELD,test,k;
   
#ifndef NDEBUG1
   printf(" SimCompi MiniMax[%d][%d] = %d\n",zl,spl,MiniMax[zl][spl]);
   fflush(stdout);
#endif
#ifndef NDEBUG
   assert(zl >= 0 && zl < 8);
   assert(spl >= 0 && spl < 8);
#endif
   if (!it) return(MiniMax[zl][spl]);
   MiniMax[zl][spl] = ~MiniMax[zl][spl];
   for (k=0;k<8;k++)
      if (MiniMax[k][spl] >= 0 && (test = SimSpieler(spl,k,it-1)) > best) best = test;
   MiniMax[zl][spl] = ~MiniMax[zl][spl];
   if (best != INT_MIN+LAST_FIELD) best += MiniMax[zl][spl];
   else if (it < maxit) best = MiniMax[zl][spl];
   return(best);
}

int SimSpieler(int spt,int zl,int it)
{
   int best = INT_MIN+LAST_FIELD,test,l;
   
#ifndef NDEBUG1
   printf(" SimSpieler MiniMax[%d][%d] = %d\n",zl,spt,MiniMax[zl][spt]);
   fflush(stdout);
#endif
#ifndef NDEBUG
   assert(zl >= 0 && zl < 8);
   assert(spt >= 0 && spt < 8);
#endif
   if (!it) return(-MiniMax[zl][spt]);
   MiniMax[zl][spt] = ~MiniMax[zl][spt];
   for (l=0;l<8;l++)
      if (MiniMax[zl][l] >= 0 && (test = SimCompi(l,zl,it-1)) > best) best = test;
   MiniMax[zl][spt] = ~MiniMax[zl][spt];
   best -= MiniMax[zl][spt];
   return(best);
}

int CompiZug(void)
{
   int best,test,besteSpalte;
   register int j;
   
   /* sucht für den Computer den nächsten besten Zug */
   best = INT_MIN;
   for (j=0;j<8;j++) {
      /* erst Test, ob das Feld noch nicht benutzt ist */
      if (MiniMax[zeile][j] >= 0 && best < (test = SimCompi(j,zeile,maxit))) {
         best = test;
         besteSpalte = j;
      }
   }
   if (best == INT_MIN+LAST_FIELD || best == INT_MIN) {
#ifndef NDEBUG2
      puts("endep = TRUE."); fflush(stdout);
#endif
      endep = LAST_FIELD;
   }
   return(besteSpalte);
}

void print3(int num,int x,int y)
{
   static char str[16]="00000000000000";
   int l;
   
   l = stci_d(str+4,num);
   Move(rp,x,y);
   Text(rp,str+l,4);
}

void print2(int num,int x,int y)
{
   static char str[4],zif[11] = "0123456789";
   
   num = ABS(num)%100;
   str[0] = zif[num/10];
   str[1] = zif[num%10];
   Move(rp,x,y);
   Text(rp,str,2);
}

extern struct Image zeilel,zeiler,spalteo,spalteu;

void highlight(int ply,int todo)
{
/*   static int sx=-1,sy=-1,cx=-1,cy=-1; */ /* speicher für später, letzte pos. */
   
   if (todo == ON) {
      if (ply == SPIELER) {
         DrawImage(rp,&spalteo,MIN_X+spalte*SPALTEN,MIN_Y);
         DrawImage(rp,&spalteu,MIN_X+spalte*SPALTEN,MAX_Y);
      } else { /* Compi */
         DrawImage(rp,&zeilel,MIN_X,MIN_Y+zeile*ZEILEN);
         DrawImage(rp,&zeiler,MAX_X,MIN_Y+zeile*ZEILEN);
      }
   } else { /* löschen */
      SetAPen(rp,0);
      if (ply == SPIELER) {
         RectFill(rp,MIN_X+spalte*SPALTEN,MIN_Y-7,MIN_X+(spalte+1)*SPALTEN,MIN_Y-1);
         RectFill(rp,MIN_X+spalte*SPALTEN,MAX_Y+1,MIN_X+(spalte+1)*SPALTEN,MAX_Y+7);
      } else {
         RectFill(rp,MIN_X-7,MIN_Y+zeile*ZEILEN,MIN_X-1,MIN_Y+(zeile+1)*ZEILEN);
         RectFill(rp,MAX_X+1,MIN_Y+zeile*ZEILEN,MAX_X+7,MIN_Y+(zeile+1)*ZEILEN);
      }
      SetAPen(rp,1);
   }
   return;
}

int getEvent(void)
{
   struct Gadget *iaddr;
   ULONG class;
   USHORT code;
   struct IntuiMessage *message;
   int flag=1,id;
   
   do {
      do {
         Wait(1L<<MMwin->UserPort->mp_SigBit);
      } while (!(message = (struct IntuiMessage *)GetMsg(MMwin->UserPort)));
      class = message->Class;
      code = message->Code;
      iaddr = (struct Gadget *)message->IAddress;
      ReplyMsg((struct Message *)message);
      if (class == CLOSEWINDOW) return(EV_QUIT);
      else {
         /* Gadgetup */
         id = iaddr->GadgetID;
         if (id%8 == spalte && MiniMax[id/8][id%8] >= 0) flag = 0;
      }
   } while(flag); /* bis ein nicht gesperrtes gadget angeklickt wird */
   return(id/8);
}

extern struct Image kreuz,kreis;

void setBenutzt(int ply)
{
   struct Image *i;
   
   SetAPen(rp,0);
   RectFill(rp,MIN_X+spalte*SPALTEN+1,MIN_Y+zeile*ZEILEN+1,SPALTEN-1+MIN_X+spalte*SPALTEN,ZEILEN-1+MIN_Y+zeile*ZEILEN);
   SetAPen(rp,1);
   i = (ply == SPIELER)? &kreuz : &kreis;
   DrawImage(rp,i,spalte*SPALTEN+MIN_X,zeile*ZEILEN+MIN_Y);
   return;
}

extern struct Requester NewGameReq;
extern char ergStr[16];

int FrageEnde(void)
{
   struct IntuiMessage *msg;
   int gad=-1;
   
   strcpy(ergStr,((CompiPkte>SpielerPkte)?"I WIN":"YOU WIN"));
   if (!Request(&NewGameReq,MMwin)) return(EV_QUIT);
   do {
      Wait(1<<MMwin->UserPort->mp_SigBit);
      if (msg = (struct IntuiMessage *)GetMsg(MMwin->UserPort)) {
         if (msg->Class == GADGETUP) {
            gad = ((struct Gadget *)msg->IAddress)->GadgetID;
         }
      }
      ReplyMsg((struct Message *)msg);
   } while(gad == -1);
   return((gad == 1)?EV_NEW_GAME:EV_QUIT);
}
    
/* Wenn MiniMax[][] == 0, dann kein negativer Wert !!! */

UWORD chip zeilee_data[1 * 5 * 2] = {
28672,63488,63488,63488,28672,0,0,0,0,0};
UWORD chip spalteu_data[1 * 6 * 2] = {
12288,12288,30720,30720,64512,64512,0,0,0,0,0,0};
UWORD chip spalteo_data[1 * 6 * 2] = {
64512,64512,30720,30720,12288,12288,0,0,0,0,0,0};
UWORD chip spielerbelegt_data[1 * 5 * 2] = {
34816,20480,8192,20480,34816,0,0,0,0,0};
UWORD chip compi_bel_data[1 * 5 * 2] = {
28672,34816,34816,34816,28672,0,0,0,0,0};

struct Image zeilel = {
   -7, 3, 5, 5, 2, &zeilee_data[0], 3, 0, NULL};
struct Image zeiler = {
   2, 3, 5, 5, 2, &zeilee_data[0], 3, 0, NULL};
struct Image spalteu = {
   6, 2, 6, 6, 2, &spalteu_data[0], 3, 0, NULL};
struct Image spalteo = {
   6, -7, 6, 6, 2, &spalteo_data[0], 3, 0, NULL};
struct Image kreuz = {
   7, 3, 5, 5, 2, &spielerbelegt_data[0], 3, 0, NULL};
struct Image kreis = {
   7, 3, 5, 5, 2, &compi_bel_data[0], 3, 0, NULL};

/*---------------------------------------------------*
  Gadgets created with PowerSource V3.0
  which is (c) Copyright 1990-91 by Jaba Development
  written by Jan van den Baard
 *---------------------------------------------------*/

SHORT SharedBordersPairs0[] = {0,0,134,0,134,33,0,33,0,0 };
SHORT SharedBordersPairs1[] = {-1,-1,71,-1,71,10,-1,10,-1,-1 };
SHORT SharedBordersPairs2[] = {-1,-1,38,-1,38,10,-1,10,-1,-1 };

struct Border SharedBorders[] = {
  0,0,3,1,JAM1,5,(SHORT *)&SharedBordersPairs0[0],NULL,
  0,0,2,0,JAM1,5,(SHORT *)&SharedBordersPairs1[0],NULL,
  0,0,2,0,JAM1,5,(SHORT *)&SharedBordersPairs2[0],NULL };

char ergStr[16];

struct TextAttr topaz = { 
   "topaz.font",
   TOPAZ_EIGHTY,
   FS_NORMAL,
   FPF_ROMFONT
};

struct IntuiText RenderTexts = {2,0,JAM1,44,5,&topaz,(UBYTE *)ergStr,NULL };
struct IntuiText Gadget2_text = {2,0,JAM1,4,1,&topaz,(UBYTE *)"Quit",NULL };
struct IntuiText Gadget1_text = {2,0,JAM1,4,1,&topaz,(UBYTE *)"New Game",NULL };

#define Gadget2_ID    2

struct Gadget Gadget2 = {
  NULL,89,19,38,10,
  GADGHCOMP,
  RELVERIFY|ENDGADGET,
  BOOLGADGET+REQGADGET,
  (APTR)&SharedBorders[2],NULL,
  &Gadget2_text,NULL,NULL,Gadget2_ID,NULL };


#define Gadget1_ID    1

struct Gadget Gadget1 = {
  &Gadget2,6,19,71,10,
  GADGHCOMP,
  RELVERIFY|ENDGADGET,
  BOOLGADGET+REQGADGET,
  (APTR)&SharedBorders[1],NULL,
  &Gadget1_text,NULL,NULL,Gadget1_ID,NULL };

struct Requester NewGameReq = {
 NULL,15,34,135,34,0,0,&Gadget1,&SharedBorders[0],&RenderTexts,
 NULL,0,NULL,NULL,NULL,NULL,NULL };
