/* Bluemoon - solitaire card game...ported to the Amiga by Tony Vallot */
/* A sloppy hack but a good learning experience....                    */
/* Unix text version released to USENET by Tim Lister                  */
/* Compiles under Manx 3.6a 'cc BlueMoon.c ... ln +c BlueMoon.o -lc'   */
/*                                       the +c forces chip loading    */
/* This program falls under the shareware category. Contributions      */
/* would be appreciated... have fun!                                   */
/*                                                                     */
/*          Tony Vallot                                                */
/*          1211 Bancroft Street #3                                    */
/*          Bellingham, WA 98225                                       */
/*                                                                     */
#include <functions.h>
#include <exec/types.h>
#include <graphics/gfxbase.h>
#include <intuition/intuition.h>

struct IntuitionBase   *IntuitionBase;
struct GfxBase         *GfxBase;
struct Screen          *screen;
struct Window          *window;
struct RastPort        *rastport;
struct ViewPort        *viewport;

ULONG  class;          /* Mouse info vars */
SHORT  mouseX, mouseY, mousedown, newgame, quit;
char   bmout[]    = { "Blue Moon - by Tim Lister - Deal Number #" };
char   numdeals[] = { "You finished the game in # deals." };
char   cardlett[] = { "A 2 3 4 5 6 7 8 9 10J Q K " };

#define Q_MINX  40L
#define Q_MINY 187L
#define Q_MAXX 128L
#define Q_MAXY 199L

#define N_MINX 512L
#define N_MINY 187L
#define N_MAXX 600L
#define N_MAXY 199L

struct NewScreen newscreen = {
   0, 0, 640, 200, 2, 0, 1, HIRES, CUSTOMSCREEN,  NULL, NULL, NULL, NULL };

struct NewWindow newwindow = {
   0, 0, 640, 200, 0, 1, MOUSEBUTTONS, BACKDROP | BORDERLESS,
   NULL, NULL, NULL, NULL, NULL, 0, 0, 0, 0, CUSTOMSCREEN };

USHORT acard [] = {
   0x7fff, 0xfffe, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
   0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
   0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
   0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
   0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
   0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0x7fff, 0xfffe };

USHORT nocard[] = {
   0x7fff, 0xfffe, 0x8000, 0x0001, 0x8000, 0x0001, 0x8000, 0x0001,
   0x8000, 0x0001, 0x8000, 0x0001, 0x8000, 0x0001, 0x8000, 0x0001,
   0x8000, 0x0001, 0x8000, 0x0001, 0x8000, 0x0001, 0x8000, 0x0001,
   0x8000, 0x0001, 0x8000, 0x0001, 0x8000, 0x0001, 0x8000, 0x0001,
   0x8000, 0x0001, 0x8000, 0x0001, 0x8000, 0x0001, 0x8000, 0x0001,
   0x8000, 0x0001, 0x8000, 0x0001, 0x8000, 0x0001, 0x7fff, 0xfffe };

USHORT selected[] = {
   0x3fff, 0xffff, 0xfc00,
   0x7fff, 0xffff, 0xfe00,
   0x7800, 0x0000, 0x1e00,
   0x7000, 0x0000, 0x0e00,
   0x7000, 0x0000, 0x0e00,
   0x7000, 0x0000, 0x0e00,
   0x7000, 0x0000, 0x0e00,
   0x7000, 0x0000, 0x0e00,
   0x7000, 0x0000, 0x0e00,
   0x7000, 0x0000, 0x0e00,
   0x7000, 0x0000, 0x0e00,
   0x7000, 0x0000, 0x0e00,
   0x7000, 0x0000, 0x0e00,
   0x7000, 0x0000, 0x0e00,
   0x7000, 0x0000, 0x0e00,
   0x7000, 0x0000, 0x0e00,
   0x7000, 0x0000, 0x0e00,
   0x7000, 0x0000, 0x0e00,
   0x7000, 0x0000, 0x0e00,
   0x7000, 0x0000, 0x0e00,
   0x7000, 0x0000, 0x0e00,
   0x7000, 0x0000, 0x0e00,
   0x7000, 0x0000, 0x0e00,
   0x7000, 0x0000, 0x0e00,
   0x7000, 0x0000, 0x0e00,
   0x7800, 0x0000, 0x1e00,
   0x7fff, 0xffff, 0xfe00,
   0x3fff, 0xffff, 0xfc00 };

/* someone could really make these look better... */
USHORT heart[] = {
   0x0000, 0x0000, 0x03f0, 0x0fc0, 0x0ffe, 0x7ff0, 0x1ffe, 0x7ff8,
   0x1fff, 0xfff8, 0x1fff, 0xfff8, 0x1fff, 0xfff8, 0x0fff, 0xfff0,
   0x07ff, 0xffe0, 0x00ff, 0xff00, 0x001f, 0xf800, 0x0007, 0xe000,
   0x0001, 0x8000, 0x0000, 0x0000 };

USHORT diamond[] = {
   0x0000, 0x0000, 0x0001, 0x8000, 0x0003, 0xc000, 0x000f, 0xf000,
   0x001f, 0xf800, 0x007f, 0xfe00, 0x01ff, 0xff80, 0x01ff, 0xff80,
   0x007f, 0xfe00, 0x001f, 0xf800, 0x000f, 0xf000, 0x0007, 0xc000,
   0x0001, 0x8000, 0x0000, 0x0000 };

USHORT spade[] = {
   0x0000, 0x0000, 0x0001, 0x8000, 0x0007, 0xe000, 0x003f, 0xfc00,
   0x00ff, 0xff00, 0x03ff, 0xffc0, 0x07ff, 0xffe0, 0x0fff, 0xfff0,
   0x1fff, 0xfff8, 0x1ffd, 0xbff8, 0x0ff9, 0x9ff0, 0x07f3, 0xcfe0,
   0x0007, 0xe000, 0x0000, 0x0000 };

USHORT club[] = {
   0x0000, 0x0000, 0x0003, 0xc000, 0x001f, 0xf800, 0x003f, 0xfc00,
   0x003f, 0xfc00, 0x001f, 0xf800, 0x01cf, 0xf380, 0x0fff, 0xfff0,
   0x1fff, 0xfff8, 0x1ffd, 0xbff8, 0x1ffd, 0xbff8, 0x0ffb, 0xdff0,
   0x01e7, 0xe780, 0x0000, 0x0000 };

USHORT *suitptr[] = { heart, spade, diamond, club };

#define BACKGROUND 0L
#define WHITE      1L
#define BLACK      2L
#define RED        3L

openall()
{
   if (!(IntuitionBase = (struct IntuitionBase *)
      OpenLibrary ("intuition.library", 0L))) {
      printf ("No Intuition!\n");
      exit (FALSE);
   }
   if (!(GfxBase = (struct GfxBase *)
      OpenLibrary ("graphics.library", 0L))) {
      printf ("No Graphics!\n");
      exit (FALSE);
   }
   if (!(screen = OpenScreen (&newscreen))) {
      printf ("No Screen!\n");
      exit (FALSE);
   }
   newwindow.Screen = screen;
   if (!(window = OpenWindow (&newwindow))) {
      printf ("No Window!\n");
      exit (FALSE);
   }
}

my_init()
{
   openall ();
   viewport = (struct ViewPort *) ViewPortAddress (window);
   rastport = &(screen->RastPort);
   SetDrMd (rastport, JAM1);
   SetAPen (rastport, WHITE);
   SetBPen (rastport, BACKGROUND);
   mousedown = FALSE;
   quit = FALSE;
}

closeall ()
{
   if (window)        CloseWindow  (window);
   if (screen)        CloseScreen  (screen);
   if (GfxBase)       CloseLibrary (GfxBase);
   if (IntuitionBase) CloseLibrary (IntuitionBase);
   exit (TRUE);
}

centerprint (x, y, str, col)
long x, y;
char str[];
long col;
{
   long len, numpixels;

   len = strlen (str);
   if (col != WHITE)
      SetAPen (rastport, col);
   numpixels = TextLength (rastport, str, len);
   x = x - (numpixels / 2L);
   Move (rastport, x, y);
   Text (rastport, str, len);
   if (col != WHITE)
      SetAPen (rastport, WHITE);
}

offcenterprint (x, y, str, frontcol, backcol)
long x, y;
char str[];
long frontcol, backcol;
{
   centerprint (x+1L, y+1L, str, backcol);
   centerprint (x, y, str, frontcol);
}

/*****************************************************************************
 *                                                                           *
 *                         B l u e   M o o n                                 *
 *                         =================                                 *
 *                                                                           *
 *                  A patience game by T.A.Lister                            *
 *                                                                           *
 *****************************************************************************/

#include <stdio.h>

#define NOCARD      -1

#define HEARTS      0
#define SPADES      1
#define DIAMONDS   2
#define CLUBS      3

#define ACE      0
#define TWO      1
#define THREE      2
#define FOUR      3
#define FIVE      4
#define SIX      5
#define SEVEN      6
#define EIGHT      7
#define NINE      8
#define TEN      9
#define JACK      10
#define QUEEN      11
#define KING      12

#define SUIT_LENGTH   13

#define GRID_WIDTH   14   /*    13+1  */
#define GRID_LENGTH   56   /* 4*(13+1) */
/*convert grid # to actual screen coordinates */
#define convertX(x) (x % GRID_WIDTH) * 40 + 45
#define convertY(y) ((y / GRID_WIDTH) * 30) + ((y / GRID_WIDTH) * 5) + 20

#define PACK_SIZE   52

int deck_size=PACK_SIZE;   /* initial deck */
int deck[PACK_SIZE];

int grid[GRID_LENGTH];   /* card layout grid */
int freeptr[4];      /* free card space pointers */

int deal_number;

init_vars()
{
   int i;

   deal_number=0;
   deck_size=PACK_SIZE;
   for(i=0;i<PACK_SIZE;i++)   deck[i]=i;
   for(i=0;i<4;i++)      freeptr[i]=i * GRID_WIDTH;
}

shuffle(size)
int size;
{
   int i,j,numswaps,swapnum,temp;

   numswaps=size*10;   /* an arbitrary figure */

   for (swapnum=0;swapnum<numswaps;swapnum++)
   {
      i=rand() % size;
      j=rand() % size;
      temp=deck[i];
      deck[i]=deck[j];
      deck[j]=temp;
   }
}

deal_cards()
{
   int ptr, card=0, value, csuit, crank, suit, aces[4];

   for (suit=HEARTS;suit<=CLUBS;suit++)
   {
      ptr=freeptr[suit];
      grid[ptr++]=NOCARD;   /* 1st card space is blank */
      while ((ptr % GRID_WIDTH) != 0)
      {
         value=deck[card++];
         crank=value % SUIT_LENGTH;
         csuit=value / SUIT_LENGTH;
         if (crank==ACE)
            aces[csuit]=ptr;
         grid[ptr++]=value;
      }
   }

   if (deal_number==1)   /* shift the aces down to the 1st column */
      for (suit=HEARTS;suit<=CLUBS;suit++)
      {
         grid[suit * GRID_WIDTH] = suit * SUIT_LENGTH;
         grid[aces[suit]]=NOCARD;
         freeptr[suit]=aces[suit];
      }
}

printcard(cd) /* altered a bit ... TV */
int cd;
{
   long x, y;
   int rank, suit;
   char out;

   x = convertX(cd);
   y = convertY(cd);
   rank=grid[cd] % SUIT_LENGTH;
   suit=grid[cd] / SUIT_LENGTH;

   if (rank == NOCARD) {
      SetDrMd (rastport, JAM2);
      BltTemplate (&nocard[0], 0L, 4L, rastport, x, y, 32L, 24L);
      SetDrMd (rastport, JAM1); /* restore normal drawing mode */
   }
   else {
      BltTemplate (&acard[0], 0L, 4L, rastport, x, y, 32L, 24L);

      SetAPen (rastport, (suit % 2) ? BLACK : RED); /* put suit symbol */
      BltTemplate (suitptr[suit], 0L, 4L, rastport, x, y+10L, 32L, 14L);

      Move (rastport, x+2L, y+7L); /* and draw the rank */
      Text (rastport, &cardlett[rank*2], 2L);

      SetAPen (rastport, WHITE); /* restore default pen */
   }
}

show_select (cd, col) /* TV */
int cd;
long col;
{
   long x, y;

   x = convertX(cd);
   y = convertY(cd);

   SetAPen (rastport, col);
   BltTemplate (selected, 0L, 6L, rastport, x-4L, y-2L, 48L, 28L);
   SetAPen (rastport, WHITE);
}

coloroff ()
{
   SetRGB4 (viewport, 0L,  0L,  2L, 10L);
   SetRGB4 (viewport, 1L,  0L,  2L, 10L);
   SetRGB4 (viewport, 2L,  0L,  2L, 10L);
   SetRGB4 (viewport, 3L,  0L,  2L, 10L);
}

coloron ()
{
   SetRGB4 (viewport, 1L, 15L, 15L, 15L);
   SetRGB4 (viewport, 2L,  0L,  0L,  0L);
   SetRGB4 (viewport, 3L, 15L,  1L,  3L);
}

makebox (minx, miny, maxx, maxy, st)
long minx, miny, maxx, maxy;
char st[];
{
   long x, y;

   RectFill (rastport, minx, miny, maxx, maxy);  /* Make the box */
   minx = minx + 2; miny++; maxx = maxx - 2; maxy--;
   SetAPen (rastport, RED); /* Draw a border in it */
   Move (rastport, minx, miny);
   Draw (rastport, maxx, miny);
   Draw (rastport, maxx, maxy);
   Draw (rastport, minx, maxy);
   Draw (rastport, minx, miny);
   Draw (rastport, maxx, miny);
   SetAPen (rastport, WHITE);

   x = ((maxx-minx) / 2L) + minx; /* Put text in it */
   y = ((maxy-miny) / 2L) + miny + 3L;
   centerprint (x, y, st, BLACK);
}

display_cards(deal)
int deal;
{
   int row, card; /* More alteration...TV */

   coloroff();
   SetRast (rastport, BACKGROUND);

   bmout[40] = (char) (deal + 48);
   offcenterprint (320L, 10L, bmout, WHITE, BLACK);

   makebox (Q_MINX, Q_MINY, Q_MAXX, Q_MAXY, "* Quit *");
   makebox (N_MINX, N_MINY, N_MAXX, N_MAXY, "New Game");

   for(row=HEARTS;row<=CLUBS;row++)
      for(card=0;card<GRID_WIDTH;card++)
         printcard(row * GRID_WIDTH + card);

   centerprint (320L, 160L, "Select card to move", WHITE);
   offcenterprint (320L, 195L, "AMIGAized by AG Vallot", RED, BLACK);

   coloron();
}

find(card)
int card;
{
   int i;

   if ((card<0)||(card>=PACK_SIZE))   return NOCARD;
   for(i=0;i<GRID_LENGTH;i++)
      if (grid[i]==card)      return i;
   return NOCARD;
}

movecard(src, dst) /* etc */
int src, dst;
{
/*   show_select (src, BACKGROUND); */
   grid[dst]=grid[src];
   grid[src]=NOCARD;

   printcard(dst);
   printcard(src);
}

inrange (minx, maxx, miny, maxy)
long minx, maxx, miny, maxy;
{
   if ((mouseX>=minx) && (mouseX<=maxx) && (mouseY>=miny) && (mouseY<=maxy))
      return (TRUE);
   else
      return (FALSE);
}

domouse ()
{
   struct IntuiMessage *mes;

   while ((mes = (struct IntuiMessage *) GetMsg(window->UserPort)) == NULL)
      Wait (1L << window->UserPort->mp_SigBit);
   class  = mes->Class;
   mouseX = mes->MouseX;
   mouseY = mes->MouseY;
   ReplyMsg (mes);

   if (class == MOUSEBUTTONS)
      mousedown = !mousedown;
   if (mousedown) {
      if (inrange(Q_MINX, Q_MAXX, Q_MINY, Q_MAXY)) /* quit */
         quit = TRUE;
      else if (inrange(N_MINX, N_MAXX, N_MINY, N_MAXY)) /* new game */
         newgame = TRUE;
   }
}

char checkmouse (s) /* TV */
int s[];
{
   SHORT  i;
   long tx, ty;
   char ret;

   ret = NOCARD;
   domouse();
   if ((class == MOUSEBUTTONS) && (mousedown)) {
      if (quit)
         ret = 'q';
      else if (newgame)
         ret = 'n';
      else
         for (i=0; i<4; i++)
            if (s[i] != NOCARD) {
               tx = convertX(s[i]);
               ty = convertY((long) s[i]);
               if (inrange (tx, tx+32L, ty, ty+24L))
                  ret = (i+'a');
            }
   }
   return (ret);
}

play_game() /* etc... */
{
   int dead=0, i, j, found;
   char c;
   int select[4], oldselect[4], card;

   for (i=0; i<4; i++)
      oldselect[i] = NOCARD;
   while ((dead<4) && (!newgame) && (!quit))
   {
      dead=0;
      for (i=0;i<4;i++)
      {
         card=grid[freeptr[i]-1];

         if (((card % SUIT_LENGTH) == KING) || (card == NOCARD))
            select[i]=NOCARD;
         else
            select[i]=find(card+1);

         if (select[i]==NOCARD)
            dead++;
      };

      if (dead<4)
      {
         /* these next two fors are here simply to keep from drawing boxes
            that are already drawn and erasing boxes that dont need it...
            gets rid of that ugly flash...but adds ugly code ;-) */
         for (i=0;i<4;i++)
            if (select[i]!=NOCARD) {
               for (found=FALSE, j=0; j<4; j++)
                  if ((oldselect[j]!=NOCARD) && (select[i]==oldselect[j])) {
                     oldselect[j] = NOCARD;
                     found=TRUE;
                  }
               if (!found)
                  show_select (select[i], RED);
            }
         for (i=0; i<4; i++)
            if (oldselect[i]!=NOCARD)
               show_select(oldselect[i], BACKGROUND);

         c = NOCARD;
         while ( ( (c<'a') || (c>'d') ) && (c!='q') && (c!='n'))
            c = checkmouse (select);

         if ((c!='q')&&(c!='n')) {
            i=c-'a';
            if (select[i]!=NOCARD)
            {
               movecard(select[i], freeptr[i]);
               freeptr[i]=select[i];
            }
         }
         for(i=0; i<4; i++)
            oldselect[i] = select[i];
      }
   }
   for (i=0; i<4; i++) /* clean up screen (all excess select boxes) */
      if (oldselect[i]!=NOCARD)
         show_select(oldselect[i], BACKGROUND);
}

my_continue () /* TV */
{
   centerprint (320L, 160L, "Select card to move", BACKGROUND);
   centerprint (320L, 170L,
      "*** New Deal - Hit Left Mouse Button to Continue ***", WHITE);
   domouse();
}

game_finished(deal) /* etc.. */
int deal;
{
   centerprint (320L, 160L, "Select card to move", BACKGROUND);

   numdeals[25] = (char) (deal + 48);
   centerprint (320L, 165L, numdeals, WHITE);

   if (deal<4)
      centerprint (320L, 175L, "This is good.", WHITE);
   else if (deal<8)
      centerprint (320L, 175L, "This is average.", WHITE);
   else
      centerprint (320L, 175L, "This is poor.", WHITE);

   while (!(quit || newgame))
      domouse ();
}

collect_discards(dn) /* only slightly altered etc.. TV */
int dn;
{
   int row, col, cardno=0, finish, gridno;

   if ((newgame) || (quit))
      return (0);
   else for (row=HEARTS;row<=CLUBS;row++) {
      finish=0;
      for (col=1;col<GRID_WIDTH;col++)
      {
         gridno=row * GRID_WIDTH + col;

         if ((grid[gridno]!=(grid[gridno-1]+1))&&(finish==0))
         {
            finish=1;
            freeptr[row]=gridno;
         };

         if ((finish!=0)&&(grid[gridno]!=NOCARD))
            deck[cardno++]=grid[gridno];
      }
   }
   domouse(); /* get last mouseup */
   if (cardno != 0)
      my_continue();
   else
      game_finished (dn);
   if ((newgame) || (quit))
      return (0);
   else
      return cardno;
}

main() /* Yep... this one too... */
{
   my_init ();

   srand((int)time((long *)0));

   while (!quit) {
      init_vars();
      newgame = FALSE;

      do{
         deal_number++;
         shuffle(deck_size);
         deal_cards();
         display_cards(deal_number);
         play_game();
      }while ((deck_size=collect_discards(deal_number)) != 0);
   }
   closeall();
}
