/********************************************

               FourTurbo V1.2
            Written by Captain Bifat
              and Ramsau of TEK

        developped on Manx Aztec-C V3.6
           and DevPac Assembler 2.11

  Compilation:

  cc fourturbo
  ln fourturbo -lc +cd

 ********************************************/

#include "fourturbo.h"

#define XSIZE 7            /* Größe des Spielfieldes horizontal*/
#define YSIZE 6            /* dito vertikal */
#define LENGTH 4           /* Length gewinnt */


/* Makros */

#define REAL(x) (((x)==1)?0:(x))


static TEXT kommentar [12] [35] =
	{
   "Move, niggers!              ",

   "Make your draw, yellow!     ",
   "Make your draw, red!        ",

   "CPU processing for yellow.  ",
   "CPU processing for red.     ",

   "CPU wins for yellow.        ",
   "CPU wins for red.           ",
   "Yellow humanoid wins.       ",
   "Red humanoid wins.          ",

   "Game ends up in remis.      ",
   "Game aborted.               ",
   "CPU votes for...            "
   };

struct IntuitionBase *IntuitionBase;
struct Window *FourWindow;
struct NewScreen *FirstScreen;
struct IntuiMessage *message;
struct RastPort *WiRaPo;

struct Message *GetMsg();
struct Gadget *GadgetPtr;

void rethinksize(), openall(), waitformessage(), analyzemessage(), bye();
void getmessage(), display(), play(), showfield(), initwindow();
void DrawImage(), SizeWindow(), MoveWindow(), RefreshWindowFrame();
void ReplyMsg(), CloseWindow(), CloseLibrary();
void OnGadget(), OffGadget(), SetWindowTitles(), SetWindowTitles();
void SetMenuStrip(), ClearMenuStrip();

UBYTE playermode[2], player, win, draw, depth, remis;
ULONG MessageClass;
USHORT code, gadnum, gadselected;
ULONG seed;
UBYTE drawcount;
LONG good_draw [XSIZE], bad_draw [XSIZE];
BYTE tiefen [XSIZE];

UBYTE dummy [11] = {1,1,1,1,1,1,1,1,1,1,1};

UBYTE field [56] =

	  {1,1,1,1,1,1,1,1,
		1,1,1,1,1,1,1,1,
		1,1,1,1,1,1,1,1,
		1,1,1,1,1,1,1,1,
		1,1,1,1,1,1,1,1,
		1,1,1,1,1,1,1,1,
		1,1,1,1,1,1,1,1};

UBYTE winners [48] =
   {3,4,5,7,5,4,3,0,4,6,8,10,8,6,4,0,5,8,11,13,11,8,5,0,5,8,11,13,11,8,5,0,4,6,8,10,8,6,4,0,3,4,5,7,5,4,3,0};


/* Zufallsgenerator */

random (from,to)
UWORD from, to;
   {
   WORD result = (int)(seed % (to - from + 1) + from);
   LONG datevec [3], *DateStamp();

   if (seed == 0L)
      {
      DateStamp (datevec);
      seed = (UWORD) (datevec [1] * datevec [2]);
      }
   else
      {
      if (seed > 0x80000000L) seed = ((seed << 1) ^ 0x1D872B41L);
      else seed <<= 1;
      }
   return (result);
   }


/* Hauptprogramm */

void main()
   {
	short int xr, yr;
	unsigned char buf[256];

   openall();

   ReSizeGadget.NextGadget = FourWindow->FirstGadget;
   FourWindow->FirstGadget = &ReSizeGadget;
   RefreshWindowFrame(FourWindow);

   WiRaPo = FourWindow->RPort;

	initwindow();

   FOREVER
      {
      waitformessage();

      switch (gadnum)
         {
         case 112:
            bye();
            break;
         case 1:
				drawcount = 0;
				OnGadget(&AbortGadget,FourWindow,NULL);
				OnGadget(&HintGadget,FourWindow,NULL);
				OffGadget(&Player2,FourWindow,NULL);
				OffGadget(&Player1,FourWindow,NULL);
				OffGadget(&PlayGadget,FourWindow,NULL);
            play();
				OnGadget(&Player2,FourWindow,NULL);
				OnGadget(&Player1,FourWindow,NULL);
				OnGadget(&PlayGadget,FourWindow,NULL);
				OffGadget(&AbortGadget,FourWindow,NULL);
				OffGadget(&HintGadget,FourWindow,NULL);
            break;
         case 14:
            playermode [0] = gadselected;
            break;
         case 15:
            playermode [1] = gadselected;
            break;
         case 321:
            rethinksize();
            break;
         default:
            break;
         }
      }
   }

void rethinksize()
   {
	int ypos = FourWindow->TopEdge;
	int xpos = FourWindow->LeftEdge;

   SizeWindow(FourWindow, -100L, -190L);
	MoveWindow(FourWindow, (long) -xpos, (long) -ypos);
	SetWindowTitles(FourWindow, -1L, 0L);
	ClearMenuStrip(FourWindow);

   FOREVER
      {
      waitformessage();

      if(gadnum == 321)
         {
         /* alles refreshen */
         MoveWindow(FourWindow,(long) xpos-FourWindow->LeftEdge, (long) ypos-FourWindow->TopEdge);
         SizeWindow(FourWindow, 100L, 190L);
         RefreshWindowFrame(FourWindow);
			initwindow();
         return();
         }
      }
   }


void initwindow()
	{
	SetWindowTitles(FourWindow, -1L, "Four in a Row Turbo V1.2       by Bifat and Ramsau of TEK");
	DrawImage(WiRaPo, &GelbStein, 7L, 149L);
	DrawImage(WiRaPo, &RotStein, 160L, 149L);
	display(0);
	OffGadget(&AbortGadget, FourWindow,NULL);
	OffGadget(&HintGadget, FourWindow,NULL);
	SetMenuStrip(FourWindow, &Menu);
	}

/* Library und Fenster schließen & beenden */

void bye()
   {
	ClearMenuStrip(FourWindow);
   if(FourWindow)
      CloseWindow(FourWindow);
   if(IntuitionBase)
      CloseLibrary(IntuitionBase);
   exit(0);
   }


/* Library & Window öffnen */

void openall()
   {
   struct Library *OpenLibrary();
   struct Window *OpenWindow();

   if(!(IntuitionBase = (struct IntuitionBase *) OpenLibrary("intuition.library", 0L)))
      bye();

   if(!(FourWindow = (struct Window *) OpenWindow(&WindowStructure)))
      bye();
   }


/* Auf eine IntuitionMessage warten, bis dahin schlafen */

void waitformessage()
   {
   waitloop:
   Wait(1L << FourWindow->UserPort->mp_SigBit);
   if((message = (struct IntuiMessage *) GetMsg(FourWindow->UserPort)) == NULL)
      goto waitloop;
   analyzemessage();
   }


/* erhaltene Message analysieren: Welches Gadget, welche Klasse etc */

void analyzemessage()
   {
   MessageClass = message->Class;
   code = message->Code;
   ReplyMsg(message);
   GadgetPtr = (struct Gadget *) message->IAddress;
   gadnum = GadgetPtr->GadgetID;
   gadselected = (((GadgetPtr->Flags) & SELECTED) == SELECTED);
   }


/* Aktuelle IntuitionMessage holen */

void getmessage()
   {
   gadnum = 0;
   message = (struct IntuiMessage *) GetMsg(FourWindow->UserPort);
   if(message != NULL)
      analyzemessage();
   }


/* Kommentartext in der Kopfzeile ausgeben */

void display(s)
BYTE s;
   {
   strcpy(Text1.IText, kommentar [s]);
   PrintIText(WiRaPo, &Text1, 0L, 0L);
   }


/*  Routinen der Zugberechnung */

void play()
   {
   int a;

   for(win = 0; win < 48; ++win)
      field [win] = 1;

	for(win = 0; win < XSIZE; ++win)
		tiefen [win] = 40 + win;

   showfield();

   win = remis = 0;
                                                /* Hauptschleife */
   do
      {
      for(player = 0; player < 2 && !win && !remis; ++player, ++drawcount)
         {
         if(playermode[player])            /* Menschenzug */
            {
            display(1 + player);

				nochmal:

            draw = input();                /* nach Einwurf fragen */
            if(draw == 255)
               {
               display(10);
               return;
               }
            if(draw == 254)                /* Hint */
               {
               display(11);
					flashhint(bestdraw(REAL(player + 1)));  /* berechnen */
					goto nochmal;
               }
            }
         else                              /* Maschinenzug */
            {
            display(3 + player);

            draw = bestdraw(REAL(player + 1));   /* Zug berechnen */
            if(draw == 255)                /* Wurde Abort gewählt ? */
               {
               display(10);
               return;
               }
            }

         field [tiefen[draw]] = REAL(player + 1);
         tiefen [draw] -= 8;

         showfield();                      /* Spielfeld zeigen */

			win = checkwin(tiefen[draw] + 8, REAL(player + 1));  /* gewonnen? */
         remis = full();                   /* alle Schächte voll? */
         }
      } while (!win && !remis);            /* nächster Zug   */

   if(win)
      display(4 + 2 * playermode [player - 1] + player);   /* Siegkommentar */
   else
      display(9);                                         /* Remiskommentar */

   }

checkthree(color)
BYTE color;
   {
   register short a, b, y, x;
   WORD anzahl = 0, frei;
   color = (!color ? 2 : 0);

   for(x = 0; x < XSIZE; ++x)
      for(y = 0; y < YSIZE; ++y)
         {
         if(x < 4)
            {
            frei = 0;
            for(a = x; field [y*8+a] != color && frei < 2 && a < x + 4; ++a)
               if(field [y*8+a] == 1)
                  frei++;
            if(a == x + 4 && frei < 2)
               anzahl++;
            }
         if(x < 4 && y < 3)
            {
            frei = 0;
            for(a = x, b = y; field [b*8+a] != color && frei < 2 && a < x + 4; ++a, ++b)
               if(field [b*8+a] == 1)
                  frei++;
            if(a == x + 4 && frei < 2)
               anzahl++;
            }
         if(x < 4 && y > 2)
            {
            frei = 0;
            for(a = x, b = y; field [b*8+a] != color && frei < 2 && a < x + 4; ++a, --b)
               if(field [b*8+a] == 1)
                  frei++;
            if(a == x + 4 && frei < 2)
               anzahl++;
            }
         }

   return (anzahl);
   }

/* Mensch nach dem Schacht fragen, in den er einwerfen will */

input()
   {
   FOREVER
      {
      waitformessage();
      if(gadnum >= 7 && gadnum <= 13)
         if(tiefen [gadnum - 7] >= 0)
            return(gadnum - 7);
      if(gadnum == 111)
         return(254);
      if(gadnum == 112)
         bye();
      if(gadnum == 2)
         return(255);
      }      
   }


/* Spielfeld anzeigen */

void showfield()
   {
   int x, y;
   
   for (y = 0; y < YSIZE; ++y)
      {
      for (x = 0; x < XSIZE; ++x)
         {
         if(!field [y*8+x])
            DrawImage(WiRaPo, &GelbStein, (long) OSLEFT + OSWIDTH * x, 12 + (long) OSTOP + OSHEIGHT * y);
         else if(field [y*8+x]==1)
            DrawImage(WiRaPo, &LeerStein, (long) OSLEFT + OSWIDTH * x, 12 + (long) OSTOP + OSHEIGHT * y);
         else
            DrawImage(WiRaPo, &RotStein, (long) OSLEFT + OSWIDTH * x, 12 + (long) OSTOP + OSHEIGHT * y);
         }
      }
   }


/* Zugvorschlag anzeigen */

flashhint(column)
int column;
	{
	int x, a;

	schleife:
	DrawImage(WiRaPo, &ColumnImage2, (long) OSLEFT + OSWIDTH * column, (long) OSTOP);
	a=checkbutton();
	DrawImage(WiRaPo, &ColumnImage1, (long) OSLEFT + OSWIDTH * column, (long) OSTOP);
	if(!a && !checkbutton())
		goto schleife;

	}


/* Hauptroutine zur Berechnung des besten Zuges aus Computersicht.
   <color> ist die "Farbe" des Spielers, für den der Zug berechnet werden
   soll. Übergibt die Schachtnummer. */

bestdraw(color)
int color;
   {
   long quality, bestquality;
   register short d1, draw1, draw2, d3, draw3, d4, draw4;
   int my_color, his_color, win, x, y, full, draw, d2;
   BYTE anzthree [XSIZE], bestthree, bestwinners;

   my_color = color;
   his_color = (!my_color ? 2 : 0);

   /* Bewertungsfeld löschen */

   for(x = 0; x < XSIZE; ++x)
      good_draw [x] = bad_draw [x] = 0;


   /*     *** Hauptroutine der Zugberechnung ***     */


   /* Prüfen, ob einzügiger Sieg oder einzügige Siegverhinderung möglich */

   for(draw1 = 0; draw1 < XSIZE; ++draw1)
      if ((d1 = tiefen [draw1]) >= 0)
         {
         win = checkwin(d1,my_color);
         if(win)
            return(draw1);
         }

   for(draw1 = 0; draw1 < XSIZE; ++draw1)
      if ((d1 = tiefen [draw1]) >= 0)
         {
         win = checkwin(d1,his_color);
         if(win)
            return(draw1);
         }

	getmessage();
	if(gadnum == 2)
     	return(255);			/* Abort angeklickt? */
	if(gadnum == 112)
		bye();					/* Quit angeklickt */

	for(draw1 = 0; draw1 < XSIZE; ++draw1)
		{

		#asm						/* LED-Blinken lassen */
		eori.b #2,$00bfe001
		#endasm

		d1 = tiefen [draw1];

		if (d1 < 0)
			bad_draw [draw1] = 10000000;
		else
			{
			field [d1] = my_color;
			tiefen [draw1] -= 8;

			for(draw2 = 0; draw2 < XSIZE; ++draw2)
				{
				d2 = tiefen [draw2];
				if(d2 > -1)
					{
					field [d2] = his_color;
					tiefen [draw2] -= 8;
					win = checkwin(d2, his_color);
					if(win)
						bad_draw [draw1] += 16856;

					for(draw3 = 0; draw3 < XSIZE; ++draw3)
						{
						d3 = tiefen [draw3];
						if(d3 > -1)
							{
							field [d3] = my_color;
							tiefen [draw3] -= 8;
							win = checkwin(d3, my_color);
							if(win)
								if(draw1 != draw3)
									good_draw [draw1] += 344;

							for(draw4 = 0; draw4 < XSIZE; ++draw4)
								{
								d4 = tiefen [draw4];
								if(d4 > -1)
									{
									win = checkwin(d4, his_color);
									if(win > 1)
										bad_draw [draw1] += 1;
									}
								}
							field [d3] = 1;
							tiefen [draw3] += 8;
							}
						}
						field [d2] = 1;
						tiefen [draw2] += 8;
					}
				}
			field [d1] = 1;
			tiefen [draw1] += 8;
			}
		}

	#asm
	ori.b #2,$00bfe001
	#endasm

	/*  *** Auswertung und Findung des besten Zuges ***  */

	/* unter den freien Schächten den besten ermitteln */

	bestquality = -20000000;
	y = 0;
	draw = random(2, 4);

	/* Schritt 1:Unter den möglichen Zügen den der besten Qualität ermitteln */

	for(x = 0; x < XSIZE; ++x)
		{
		quality = -21000000;
		if(bad_draw[x] < 20000000)
			{
			quality = good_draw [x] - bad_draw [x];
			if(quality >= bestquality)
				bestquality = quality;
			}
		}

	/* Schritt 2:Anzahl der Züge der maximalen Qualität ermitteln */

	for(x = 0; x < XSIZE; ++x)
		if(good_draw [x] - bad_draw [x] == bestquality)
			{
			y++;
			draw = x;
			}

	/* Schritt 3: Falls mehrere von bester Qualität vorhanden sind,
		versuchen, einen von ihnen zu finden, der die meisten
		Dreierreihen bringt */

	if(y > 1)
		{

		/* a) Anzahl der erzielbaren Dreierriehen für jeden Schacht
				bester Qualität ermitteln */

		d2 = draw = 0;
		bestthree = -1;

		for(x = 0; x < XSIZE; ++x)
			{
			if(good_draw [x] - bad_draw [x] == bestquality)
				{
				d1 = tiefen [x];
				field [d1] = my_color;
				win = checkthree(my_color);
				anzthree [x] = win;
				if(win > bestthree)
					bestthree = win;
				field [d1] = 1;
				}
			else
				anzthree [x] = -1;
			}

	/* b) Anzahl der Schächte mit den meisten Dreiern ermitteln */

	for(x = 0; x < XSIZE; ++x)
		if(bestthree != -1 && anzthree [x] == bestthree)
			{
			d2++;
			draw = x;
			}

	/* c) Bringen alle besten Schächte gleich viele Dreierreihen?
			Dann vergiß das mit den Dreierreihen!
			Andernfalls per Zufall einen von denen nehmen, die am meisten
			Dreierreihen bringen */

	if(bestthree > 0 && d2 != y)
		{
		if(d2 != 1)             /* überhaupt mehr als nur einer da? */
			{
			do
				{
				draw = random(0, 6);
				}
			while(good_draw [draw] - bad_draw [draw] != bestquality || anzthree [draw] != bestthree);
			}
		}
	else
		{

		/* Schacht mit meisten Gewinnreihen ermitteln */

		bestwinners = -1;

		for(x = 0; x < XSIZE; ++x)
			if(good_draw [x] - bad_draw [x] == bestquality)
				if((d2 = tiefen [x]) >= 0)
					if((d2 = winners [d2]) > bestwinners)
						{
						bestwinners = d2;
						draw = x;
						}
			}
		}

	brutales_raus:

	return (draw);
	}


/* ermittelt, ob alle Schächte voll sind. (Remisstellung) */

full()
   {
   UBYTE x, full;

   for(x = 0, full = 0; x < XSIZE; ++x)
      if(tiefen [x] < 0)
         ++full;
   
   return ((full == XSIZE));
   }



/* Ermittelt, ob Siegreihe vorliegt.
	Aufruf:  checkwin(abspos, color)   */

#asm

	public	_checkwin
_checkwin:

	movem.l	d1/d2/d7/a0,-(sp)			; Register retten
	lea	_field,a0						; Anfangsadresse von _field in a0
	moveq	#0,d7								; d7 löschen, schneller als clr
	move.w	20(sp),d0					; Absolutposition des letzten Zuges holen
	move.w	22(sp),d7					; Farbe holen

	move.w	d0,d1
	moveq.l	#2,d2
loop1	add.w	#9,d1
	cmp.b	0(a0,d1),d7
	dbne	d2,loop1
	bne	l1b
	moveq	#3,d0
	bra end
l1b	move.w	d0,d1
loop1b	sub.w	#9,d1
	cmp.b	0(a0,d1),d7
	dbne	d2,loop1b
	bne	l2
	moveq	#3,d0
	bra end

l2	move.w	d0,d1
	moveq.l	#2,d2
loop2	addq.w	#7,d1
	cmp.b	0(a0,d1),d7
	dbne	d2,loop2
	bne	l2b
	moveq	#4,d0
	bra end
l2b	move.w	d0,d1
loop2b	subq.w	#7,d1
	cmp.b	0(a0,d1),d7
	dbne	d2,loop2b
	bne	l3
	moveq	#4,d0
	bra end

l3	move.w	d0,d1
	moveq.l	#2,d2
loop3	addq.w	#1,d1
	cmp.b	0(a0,d1),d7
	dbne	d2,loop3
	bne	l3b
	moveq	#2,d0
	bra end
l3b	move.w	d0,d1
loop3b	subq.w	#1,d1
	cmp.b	0(a0,d1),d7
	dbne	d2,loop3b
	bne	l4
	moveq	#2,d0
	bra end

l4	move.w	d0,d1
	moveq.l	#2,d2
loop4	add.w	#8,d1
	cmp.b	0(a0,d1),d7
	dbne	d2,loop4
	bne	loose
	moveq	#1,d0
	bra end

loose	moveq	#0,d0
end	movem.l	(sp)+,d1/d2/d7/a0			; Register rückspeichern
	rts


	cseg
#endasm

#asm

; checkbutton prüft innerhalb einer Verzögerungsschleife, ob der Mausknopf
; gedrückt wird (wird von flashhint benutzt)

	public	_checkbutton
_checkbutton:
	move.l #10000,d0			; 1 Million Durchläufe zur Verzögerung
loopmb	btst.b #6,$bfe001		; #7 liefert Joystick-Port 2
	dbeq d0,loopmb					; dbeq beeinflußt Flags nicht
	beq pressed						; raus, wenn Knopf gedrückt
	moveq #0,d0						; 0 -> Knopf nicht gedrückt
	rts
pressed	moveq #1,d0				; 1 -> Knopf nicht gedrückt
	rts


	cseg

#endasm
