/*
 * Puzzle
 * ~~~~~~
 * Copyright © 1989  Oo
 * by		    o.·°
 * Walter Dörwald ·°·o·.o
 *		      O
 */

#include <exec/types.h>
#include <intuition/intuition.h>
#include <graphics/rastport.h>
#include <proto/dos.h>


/* Monitor als Zufallsgenerator! */
#define RNDREG ((UBYTE *)0xDFF007)
#define rand1() ((*RNDREG)&112)
#define rand2() (((*RNDREG)&6)>>1)

/* Auch für die, die tatsächlich     */
/* noch die Workbenchfarben benutzen */
#ifndef ORG
#define DUNK 0
#else
#define DUNK 2
#endif

/* Menü-returns */
#define G_NIX 0
#define G_END 1
#define G_OK  2

/* Grafik, wird automatisch ins ChipMem geladen */
extern UWORD Tile_Data[];
extern UWORD Pic_Data[];

struct TextAttr TA_NORM =
{
   (UBYTE *)"topaz.font",TOPAZ_EIGHTY,FSF_BOLD,FPF_ROMFONT
};

struct Image Tile =
{
   11,14,32,16,2,NULL,0x3,0x0,NULL
};

struct Image Pic =
{
   5,11,148,74,2,Pic_Data,0x3,0x0,NULL
};

struct Gadget Start =
{
   NULL,95,71,43,9,GADGHCOMP,RELVERIFY,BOOLGADGET,NULL,NULL,NULL,0L,NULL,2,NULL
};

struct Gadget OK =
{
   &Start,20,71,43,9,GADGHCOMP,RELVERIFY,BOOLGADGET,NULL,NULL,NULL,0L,NULL,1,NULL
};

struct NewWindow NewPuzzleWin =
{
   300,100,158,98,DUNK,1,MOUSEBUTTONS|CLOSEWINDOW,
   RMBTRAP|ACTIVATE|WINDOWDRAG|WINDOWDEPTH|WINDOWCLOSE,
   NULL,NULL,(UBYTE *)" Puzzle ",NULL,NULL,0,0,0,0,WBENCHSCREEN
};

struct Window *PuzzleWin;
struct RastPort *RP;
UWORD Count;

UBYTE *Zaehl = "Züge: ·····";
#define LEN 11L


UBYTE Brett[] =
{
   01,02,03,04,
   05,06,07,08,
   09,10,11,12,
   13,14,15,00
};

UBYTE Fill[] =
{
   00,01,02,03,07,11,15,14,13,12,08,04,05,06,10,09,
   09,10,06,05,04,08,12,13,14,15,11,07,03,02,01,00,
   00,15,01,14,02,13,03,12,07,08,11,04,10,05,09,06,
   06,09,05,10,04,11,08,07,12,03,13,02,14,01,15,00,
   01,00,04,05,09,08,12,13,14,15,11,10,06,07,03,02,
   00,03,15,12,01,07,14,08,02,11,13,04,05,06,10,09,
   00,07,14,05,12,03,10,01,08,15,06,13,04,11,02,09,
   00,02,05,07,08,10,13,15,01,03,04,06,09,11,12,14
};


/* Brauch ma niat */
void MemCleanup(){}


/* Libraries */
#define INTUITION_LIB 0L
#define GRAPHICS_LIB 0L
#include "IN.lib().c"


/* Deklarationen */
UBYTE Fertig(void);
UBYTE Schieb(UBYTE);
void Set(UBYTE,UBYTE);
void SetBrett(void);
void Zug(UWORD);
UBYTE Menu(UBYTE);
void Mix(void);
void _main(void);

void _main()

{
    register struct IntuiMessage *IMess;
    ULONG MessClass;
    UWORD Code;
    register UBYTE X,
		   Y,
		   ready = FALSE;


   /* Bib's auf */
   OpenLib();

   /* Steine mixen */
   Mix();

   if (PuzzleWin = OpenWindow(&NewPuzzleWin))
   {
      /* RastPort für schnellen Zugriff */
      RP = PuzzleWin->RPort;
      /* Und jetzt die ganze Grafik vorbereiten */
      SetFont(RP,OpenFont(&TA_NORM));
      SetAPen(RP,1);
      RectFill(RP,5,11,152,94);
      SetAPen(RP,3);
      RectFill(RP,5,11,152,11);
      RectFill(RP,5,11,7,94);
      SetAPen(RP,DUNK);
      SetBPen(RP,1);
      /* Züge setzen und ausgeben */
      Zug(Count = 0);
      /* Brett zeichnen */
      SetBrett();

      do
      {
	 /* Warten auf Nachricht */
	 WaitPort(PuzzleWin->UserPort);
	 while (IMess = (struct IntuiMessage *)GetMsg(PuzzleWin->UserPort))
	 {
	    /* Wichtigstes merken; Nachricht beantworten */
	    MessClass = IMess->Class;
	    Code = IMess->Code;
	    X = (UBYTE)IMess->MouseX;
	    Y = (UBYTE)IMess->MouseY;
	    ReplyMsg((struct Message *)IMess);

	    /* Verschieben */
	    if (MessClass == MOUSEBUTTONS)
	    {
	       switch(Code)
	       {
		  case SELECTDOWN:
		     /* Test ob innerhalb Grenzen */
		     if (X>=12 && X<=150 && Y>=14 && Y<=80)
		     {
			X -= 12;
			Y -= 14;
			if ((X % 36)<26 && (Y % 18)<14)
			{
			   X /= 36;
			   Y /= 18;
			   /* Verschieben und mitzählen */
			   if (Schieb((UBYTE)(X+(Y<<2))))
			   {
			      Zug(++Count);
			      if (Fertig())
				 ready = Menu(TRUE);
			   }
			}
		     }
		     break;
		  case MENUDOWN:
		     ready = Menu(FALSE);
	       }
	    }
	 }
      } while (MessClass != CLOSEWINDOW && ready != G_END);

      /* Fenster zu */
      CloseWindow(PuzzleWin);
   }

   /* Bib's zu */
   CloseLib();

   /* Das war's */
}


void Set(Pos,Num)

register UBYTE Pos,
	       Num;

{
   /* Imagezeiger neu setzen */
   /* Grafik benötigt 128 Bytes (Shift 6, da UWORD) */
   Tile.ImageData = Tile_Data + (ULONG)(Num<<6);
   /* Zeichnen (Offset steht in ImageStruktur) */
   DrawImage(RP,&Tile,(Pos&3)*36,(Pos>>2)*18);
}

UBYTE Schieb(Pos)

register UBYTE Pos;

{
   /* Pos nicht oben und oben leer ? */
   if ((Pos>3) && (!Brett[Pos-4]))
      Set((UBYTE)(Pos-4),Brett[Pos-4] = Brett[Pos]);
   /* Pos nicht rechts und rechts leer ? */
   else if (((Pos&3)<3) && (!Brett[Pos+1]))
      Set((UBYTE)(Pos+1),Brett[Pos+1] = Brett[Pos]);
   /* Pos nicht unten und unten leer ? */
   else if ((Pos<12) && (!Brett[Pos+4]))
      Set((UBYTE)(Pos+4),Brett[Pos+4] = Brett[Pos]);
   /* Pos nicht links und links leer ? */
   else if ((Pos&3) && (!Brett[Pos-1]))
      Set((UBYTE)(Pos-1),Brett[Pos-1] = Brett[Pos]);
   /* dann hald niat */
   else
      return(FALSE);

   /* Leeres Feld neu zeichen */
   Set(Pos,Brett[Pos] = 0);
   return(TRUE);
}


UBYTE Fertig()

{
   register UBYTE i;

   /* Alle Felder testen */
   for (i = 0;i<16;i++)
   {
      /* Liegt Stein falsch ? */
      if (Brett[i] != ((i+1)&15))
	 return(FALSE);
   }
   return(TRUE);
}

void SetBrett()

{
   register UBYTE  i,
		   j,
		  *FillPtr;

   /* Grafik wiederherstellen */
   SetAPen(RP,1);
   RectFill(RP,8,12,152,84);
   SetAPen(RP,3);
   RectFill(RP,5,11,152,11);
   RectFill(RP,5,11,7,94);
   SetAPen(RP,DUNK);
   /* Setzmodus per Zufall */
   FillPtr = Fill + rand1();

   /* Leere Felder malen */
   for (i = 0;i<16;i++)
   {
      j = FillPtr[i];
      RectFill(RP,9+((j&3)*36),13+((j>>2)*18),40+((j&3)*36),28+((j>>2)*18));
      Set(j,0);
      Delay(3);
   }
   /* Steine setzen */
   for (--i;i != 0xFF;i--)
   {
      j = FillPtr[i];
      Set(j,Brett[j]);
      Delay(3);
   }
}



UBYTE Menu(flag)

UBYTE flag;

{
   register struct IntuiMessage *IMess;
   register ULONG MessClass;
   register UWORD ID;
   register UBYTE ret = G_NIX;

   /* Grafik zeichnen */
   DrawImage(RP,&Pic,0,0);
   /* Gadget verwursten */
   if (flag)
      OK.Flags |= GADGDISABLED;
   else
      OK.Flags &= (!GADGDISABLED);
   /* Gadgets ein */
   AddGList(PuzzleWin,&OK,-1,2,NULL);
   RefreshGList(&OK,PuzzleWin,NULL,2);
   /* Und jetzt auf Gadgets warten */
   ModifyIDCMP(PuzzleWin,GADGETUP|CLOSEWINDOW);
   /* Warten auf Nachricht */
   do
   {
      WaitPort(PuzzleWin->UserPort);
      while (IMess = (struct IntuiMessage *)GetMsg(PuzzleWin->UserPort))
      {
	 /* Wichtigstes merken; Nachricht beantworten */
	 MessClass = IMess->Class;
	 ID = ((struct Gadget *)(IMess->IAddress))->GadgetID;
	 ReplyMsg((struct Message *)IMess);

	 /* SchließGadget ? */
	 if (MessClass == CLOSEWINDOW)
	    ret = G_END;
	 /* Oder OK bzw. Start ? */
	 else
	    ret = G_OK;
      }
   } while (!ret);
   /* Gadgets aus */
   ModifyIDCMP(PuzzleWin,MOUSEBUTTONS|CLOSEWINDOW);
   RemoveGList(PuzzleWin,&OK,2);
   /* Start, dann mixen */
   if ((ret == G_OK) && (ID == 2))
   {
      Zug(Count = 0);
      Mix();
   }
   /* Steine wieder setzen */
   if (ret != G_END)
      SetBrett();
   return (ret);
}

void Zug(Count)

register UWORD Count;

{
   register UBYTE *i;

   /* Alle Zeichen durch */
   for (i = Zaehl+10;i>Zaehl+5;i--)
   {
      /* Ziffer setzen */
      *i = '0' + (UBYTE)(Count % 10);
      /* Count/10 für nächste Abfrage und testen */
      Count /= 10;
   }
   /* Ausgeben */
   Move(RP,37,92);
   Text(RP,Zaehl,LEN);
}

void Mix()

{
   register UWORD i;
   register UBYTE Pos;

   /* Leeres Feld finden */
   for (Pos = 0;Pos<16;Pos++)
      if (!Brett[Pos])
	 break;

   /* Mixen, 1500× dürfte reichen (und geht auch noch schnell) */
   for (i = 0;i<1500;i++)
   {
      switch (rand2())
      {
	 case 0: /* Oben */
	    if (Pos>3)
	    {
	       Brett[Pos] = Brett[Pos -= 4];
	       Brett[Pos] = 0;
	       break;
	    }
	 case 1: /* Rechts */
	    if ((Pos&3) < 3)
	    {
	       Brett[Pos] = Brett[++Pos];
	       Brett[Pos] = 0;
	       break;
	    }
	 case 2: /* Unten */
	    if (Pos<12)
	    {
	       Brett[Pos] = Brett[Pos += 4];
	       Brett[Pos] = 0;
	       break;
	    }
	 case 3: /* Links */
	    if (Pos&3)
	    {
	       Brett[Pos] = Brett[--Pos];
	       Brett[Pos] = 0;
	    }
	    break;
      }
   }
}
