/************************************************************************/
/*  Hoser BackGammon version 1.0					*/
/*	Robert Pfister							*/
/*	Rfd#3 Box 2340		home:(207)-873-3520			*/
/*	Waterville, Maine 04901						*/
/*									*/
/*	Pfister_rob%dneast@dec.decwrl					*/
/*									*/
/*  Copyright  June,1987 all rights reserved.				*/
/*									*/
/*  This program will play a game of backgammon at the novice level	*/
/*									*/
/*  The code is in 4 parts...						*/
/*	 1) back.c     - main driver					*/
/*   /   2) eval.c     - evaluation of moves				*/
/* \/	 3) backscn.c  - screen stuff..					*/
/*	 4) backmenu.c - menu stuff, help text, and ``decoder''		*/
/*									*/
/* this was compiled under Manx 3.20a, using long integers		*/
/*									*/
/* This file contains:							*/
/*	gen_points	PutIntuiText	ITextLen	Gsetup		*/
/*	ShowDice	PutMoveNumber	PutPiece	decode		*/
/*	DoMenuStrip	UndoMenuStrip	requestor	finit		*/
/*	PutBarPiece	PutSpike	BlinkPiece	TextScreen	*/
/************************************************************************/
/* Most recent modification:	 3/ 8/93				*/
/* Most recent modification:	11/28/94				*/
/* Most recent modification:	 2/24/95				*/

#include <proto/intuition.h>
#include <proto/dos.h>
#include <proto/exec.h>
#include <proto/graphics.h>
#include <proto/intuition.h>
#include <proto/diskfont.h>
#include <clib/alib_protos.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>
#include "back.h"

#define max_vector	16		/* for AreaMove/Draw			*/
#define piece_width	32
#define sign(x)		(x < 0 ? -1 : +1)

typedef
  struct {
    SHORT	x,y;}	coord;

typedef
	/* order of points in box[] is upper left, lower right or		*/
	/* left, top, right, bottom as individual values			*/
	/* Spike entry is coordinates of spike point				*/
  struct {
    coord		box[2];
    coord		spike;} point_rec;
typedef
  struct {
    short	count,
		height;
    coord	point[13];}	shape_rec;

static const shape_rec
  laced_piece =
    {12, 23,
	{{ 13,  2}, { 11,  6}, {  4,10}, { -4,10},
	 {-11,  6}, {-13,  2}, {-13,-2}, {-11,-6},
	 { -4,-10}, {  4,-10}, { 11,-6}, { 13,-2}}},
  norm_piece =
    {12, 11,
	{{ 13, 1}, { 11, 3}, {  3, 5}, { -3, 5},
	 {-11, 3}, {-13, 1}, {-13,-1}, {-11,-3},
	 { -3,-5}, {  3,-5}, { 11,-3}, { 13,-1}}};
static point_rec	point[26];
int			minY;			/* has to be global		*/
static int		xc, yc,			/* center of board		*/
			minX, maxX,		/* horizontal limits		*/
			/*minY,*/ maxY,		/* vertical limits		*/
			h_pitch,		/* width of spike		*/
			height,			/* spike height			*/
			move_num_left,		/* where move number starts	*/
			move_num_len,		/* how long it plots		*/
			bar_left, bar_right;	/* limits of bar		*/
static  shape_rec	*piece_ptr = &norm_piece;
UWORD			color_table[] =
	{0x407,0xDDD,0x0F0,0xFF0,0xF00,0x000,0x00F,0xEEE};
struct ColorMap		color_map =
	{0,0,sizeof(color_table) / 2,(APTR)&color_table};
char			MyTitle[] =
	 "Hoser BackGammon     Copyright June 1987     Robert Pfister";
struct RastPort		*rp;
struct Screen		*screen = NULL;
static struct ViewPort	*vp;
extern struct Menu	MyMenu[];  /* get the menu somewhere else */
struct Window		*w = NULL;
struct TextAttr		font[] = {{"topaz",8,0,0},{"courier.font",13,0,0}};
struct TextFont		*tf = NULL;

struct NewScreen	ns = {
    0,0,-1,-1,3, 0,1, HIRES, CUSTOMSCREEN,font, MyTitle, NULL,NULL};
struct  NewWindow	nw = {
    0,10,-1,190,0,1, CLOSEWINDOW | MOUSEBUTTONS | MENUPICK,
    SMART_REFRESH | ACTIVATE | WINDOWDRAG | WINDOWDEPTH, NULL,NULL,
    "Backgammon anyone?", NULL,NULL,  0,0,640,200,CUSTOMSCREEN };
struct  NewWindow	crnw = {
    20,0,405,0,0,1,	/* top and height set when used */
    MOUSEBUTTONS, SMART_REFRESH | ACTIVATE | WINDOWDRAG,
    NULL,NULL, "Backgammon credits", NULL,NULL, 0,0,640,200,CUSTOMSCREEN };
static short		AreaBuf[max_vector * 5 / sizeof(short)];
static APTR		TBuf;
static struct TmpRas	TRas;
static struct AreaInfo	AInfo;
BOOL			laced;

void gen_points(struct Window *wp)
{				/* adjust board for screen/window size	*/
  int		bar_width,	/* the obvious				*/
		d, n;
  point_rec	*prp;

  minX = 3;
  maxX = wp->Width - 3;
  bar_width = 8;
  h_pitch = (maxX - minX - bar_width) / 12;
  bar_width = maxX - minX - 12 * h_pitch;
  if (bar_width > 10) {
    minX += (bar_width - 10) / 2;
    maxX -= (bar_width - 10) / 2;
    bar_width = maxX - minX - 12 * h_pitch;
  }
  xc = (minX + maxX) / 2;
  bar_left = xc - bar_width / 2;
  bar_right = bar_left + bar_width;

  minY = screen->Font->ta_YSize * 3 + 1;
  maxY = wp->Height - 4;
  height = ((maxY - minY) * 3 / 7);
  yc = (minY + maxY) / 2;

  d = 0;
  prp = point + 1;
  for (n = 0; n < 12; n++) {
    prp->box[1].x = (prp->box[0].x = minX + n * h_pitch + d) + h_pitch - 1;
    prp->spike.y  =
    prp->box[0].y = (prp->box[1].y = maxY) - height;
    prp->spike.x  = prp->box[0].x + h_pitch / 2;
    if (n == 5)
      d = bar_width;
    prp++;
  }

  for (n = 11; n >= 0; n--) {
    prp->box[1].x = (prp->box[0].x = minX + n * h_pitch + d) + h_pitch - 1;
    prp->box[0].y = minY;
    prp->spike.y  =
    prp->box[1].y = (prp->box[0].y = minY) + height;
    prp->spike.x  = prp->box[0].x + h_pitch / 2;
    if (n == 6)
      d = 0;
    prp++;
  }

  prp = point;
  prp->box[0].x = xc - 10;
  prp->box[0].y = maxY - height;
  prp->box[1].x = xc + 10;
  prp->box[1].y = yc + 5;
  prp->spike.x  = xc;
  prp->spike.y  = yc + 9;

  prp = point + 25;
  prp->box[0].x = xc - 10;
  prp->box[0].y = maxY + height;
  prp->box[1].x = xc + 10;
  prp->box[1].y = yc - 5;
  prp->spike.x  = xc;
  prp->spike.y  = yc - 9;
}

void PutIntuiText(char *s, int x, int y, int color)
{
  struct IntuiText	it = {1, 0, JAM2, 0, 0, NULL, NULL, NULL};

  it.FrontPen = color;
  it.LeftEdge = x;
  it.TopEdge = y;
  it.IText = s;
  PrintIText(w->RPort, &it, 0, 0);
}

int ITextLen(char *s)
{
  struct IntuiText	it = {1, 0, JAM2, 0, 0, NULL, NULL, NULL};

  it.IText = s;
  return IntuiTextLength(&it);
}

void Gsetup(void)
{
  struct Preferences	prefs;

  GetPrefs(&prefs, sizeof prefs);
  if (laced = (prefs.LaceWB & LACEWB)) {
    ns.ViewModes |= LACE;
    ns.Font = &font[1];
    tf = OpenDiskFont(&font[1]);
    piece_ptr = &laced_piece;
  }
  if (!(nw.Screen = screen = OpenScreen(&ns))) {
    finit();
    Alert(1);
    exit(1);
  }

  nw.Width = screen->Width;
  nw.TopEdge = screen->TopEdge + screen->Font->ta_YSize + 2;
  nw.Height = screen->Height - nw.TopEdge;
  if (!(w = OpenWindow(&nw))) {
    finit();
    Alert(2);
    exit(2);
  }

  vp = &screen->ViewPort;
  rp = w->RPort;
  /* intialize temporary area space */
  InitArea(&AInfo, AreaBuf, max_vector);
  rp->AreaInfo = &AInfo;
  if (!(TBuf = (APTR)AllocRaster(screen->Width, screen->Height))) {
    finit();
    Alert(3);
    exit(3);
  }

  gen_points(w);
  SetMenuStrip(w,MyMenu);
  rp->TmpRas = &TRas;
  InitTmpRas(&TRas, (char *)TBuf, RASSIZE(screen->Width, screen->Height));
  LoadRGB4(vp, color_table, 8);
  SetDrMd(rp, JAM1);

  SetAPen(rp, back_color);
  RectFill(rp, minX, minY, maxX - 1, maxY);

  SetAPen(rp, tm1_color);
  RectFill(rp, bar_left, minY, bar_right, maxY);
  /* write evaluation count at top of window	*/
  move_num_len = ITextLen("Number of Moves Evaluated: ");
  move_num_left = minX + 2 + move_num_len;
  PutIntuiText("Number of Moves Evaluated:", minX + 2,
	minY - screen->Font->ta_YSize - 2, dice_color);
  {
    struct DateStamp	time;

    DateStamp(&time);
    srand(time.ds_Tick);
  }
}

void ShowDice(BYTE  d[4], int c)
{					/* put dice on the screen	*/
  int			yu, yl;
  char			line[] = "      ";
  struct IntuiText	it = {1, 0, JAM1, 0, 0, NULL, NULL, NULL};

  /* cover up dice with dice color */
  yu = yc - screen->Font->ta_YSize / 2;
  yl = yu + screen->Font->ta_YSize;
  SetAPen(rp, dice_color);
  RectFill(rp, xc-27, yu , xc+27, yl);

  if (d[0] && d[1]) {
    /* put in the numbers for the left and right die */
    it.FrontPen = piece2_color;
    if (c == Uside)
      it.FrontPen = piece1_color;
    line[0] = '0' + d[0];
    line[4] = '0' + d[1];
    it.LeftEdge = xc - piece_width / 2;
    it.TopEdge = yu + 1;
    it.IText = line;
    PrintIText(w->RPort, &it, 0, 0);
    SetAPen(rp, tm1_color);
    RectFill(rp, bar_left, yu, bar_right - 1, yl);
  }
}

void PutMoveNumber(int Number)
{ /* only calls are from EVAL */
  char Line[8];

  sprintf(Line,"%5d  ",Number);
  PutIntuiText(Line, move_num_left,
	minY - screen->Font->ta_YSize - 2, dice_color);
}

void PutPiece(long x, long y, long color)
{ /* coords are for center of piece	*/
  int		i;
  shape_rec	*sp = piece_ptr;

  SetAPen(rp,color);
  AreaMove(rp, x + sp->point[sp->count - 1].x, y + sp->point[sp->count - 1].y);
  for (i = 0; i < sp->count; i++)
    AreaDraw(rp, x + sp->point[i].x, y + sp->point[i].y);
  AreaEnd(rp);
}

int decode(int x, int y) /* return the 'spike' number given the screen		*/
{			 /* point relative to upper left corner of the window	*/
  int	point,
	disp = 0,
	font_height = screen->Font->ta_YSize;

  if (x > xc)
    disp = bar_right - bar_left;
  /* check if dice 'clicked'...indicate unusual end of turn */
  if ((y > (yc - font_height / 2)) && (y < (yc + font_height / 2)) &&
	(x > xc - 27) && (x < xc + 27))
    return(-1);
  /* check if 'bar' selected */
  if ((x > (xc - piece_width / 2)) && (x < (xc + piece_width / 2))  &&
	( y > (font_height  - 2 - piece_ptr->height)) &&
	(y < (yc - font_height / 2 - 2)))
    point = 0;
  else
    point = 1 + (x + disp - minX) / h_pitch;
  if ( y < yc)
    point = 25 - point;
  return(point);
}

static int ErrSet=FALSE;

void DoMenuStrip(char errmsg[])
{
  ErrSet=TRUE;
  SetWindowTitles(w, (char *)-1, errmsg);
}

void UnDoMenuStrip(void)
{
  if (ErrSet == TRUE) {
    SetWindowTitles(w, (char *)-1, MyTitle);
    ErrSet = FALSE;
  }
}

/* put up a plain old requestor given three strings
	name...	the question/statement of the requestor
	yes...	the affirmative response
	no...	the negative response
    this returns 'true' for the yes, 'false' for the no  */

BOOL requestor(char name[], char yes[], char no[])
{
  const static struct IntuiText
	Rt = {tm1_color,    tm2_color,    JAM2, 50, 10, NL, (char *)0, NL},
	Rr = {spike2_color, spike1_color, JAM1,  8,  3, NL, (char *)0, NL};
  struct IntuiText	Rbody, Rright, Rleft;

  Rbody = Rt;		Rright = Rr;		Rleft = Rr;
  Rleft.IText = no;	Rright.IText = yes;	Rbody.IText = name;
  return(AutoRequest(w, &Rbody, &Rleft, &Rright, 0, 0, 300, 90));
}

extern you_me_rec	you, me;
extern USHORT		total_games;

void finit(void)
{
  char	*s[] = {
	"Running totals",
	"\n",
	"\n",
	" Total games xxxxxx\n",
	"                    You          Me\n",
	"   Dice total xxxxxxxxxx  xxxxxxxxxx\n",
	" Average roll       xx.x        xx.x\n",
	"   Incomplete xxxxxxxxxx  xxxxxxxxxx\n",
	"       Wasted xxxxxxxxxx  xxxxxxxxxx\n",
	"\n",
	" [click in window to terminate] \n"};

  if (total_games) {
    sprintf(&s[3][13], "%6d\n", total_games);
    sprintf(&s[5][14], "%10d  %10d\n", you.total.roll, me.total.roll);
    sprintf(&s[6][20], "%4.1f        %4.1f\n",
	(float)you.total.average_roll / total_games,
	(float)me.total.average_roll / total_games);
    sprintf(&s[7][14], "%10d  %10d\n", you.total.incomplete, me.total.incomplete);
    sprintf(&s[8][14], "%10d  %10d\n", you.total.wasted, me.total.wasted);
    TextScreen(s, 11);
  }

  if (TBuf)	FreeRaster(TBuf, screen->Width, screen->Height);
  if (w)	CloseWindow(w);
  if (screen)	CloseScreen(screen);
  if (laced)	CloseFont(tf);
}

void PutBarPiece(int y, int pieces, int inc, int color)
{
  char			line[4];
  struct IntuiText	it = {1, 0, JAM1, 0, 0, NULL, NULL, NULL};

  SetAPen(rp, back_color);
  RectFill(rp, xc - piece_width, y, xc + piece_width, y + inc);
  SetAPen(rp, tm1_color);
  RectFill(rp, bar_left, y, bar_right - 1, y + inc);
  pieces = abs(pieces);
  if (pieces != 0) {
    PutPiece(xc, y + inc / 2, color);
    if (pieces > 1) {
      sprintf(line, "%d", pieces);
      it.FrontPen = dice_color;
      it.LeftEdge = bar_left;
      it.TopEdge = y + 2;
      it.IText = line;
      PrintIText(w->RPort, &it, 0, 0);
    }
  }
}

void PutSpike(int spk, int pieces)
{
  long	color, inc, x, y;
  char	line[4];

  /* pick color of the pieces */
  if (pieces < 0)
    color = piece1_color;
  else
    color = piece2_color;
  inc = - piece_ptr->height;
  if (spk > 12)
      inc = -inc;

  if (spk == 0)				/* plot my ones on the bar	*/
    PutBarPiece(yc + screen->Font->ta_YSize, pieces, -inc, color);
  else if (spk == 25)			/* plot yours on the bar	*/
    PutBarPiece(yc - screen->Font->ta_YSize - inc, pieces, inc, color);
  else {				/* all the rest			*/
    int		i;
    point_rec	*pp;

    pp = &point[spk];
    /* box in the area around the spike with background color...*/
    SetAPen(rp, back_color);
    RectFill(rp, pp->box[0].x, pp->box[0].y, pp->box[1].x, pp->box[1].y);

    if (spk & 1)
      SetAPen(rp, spike1_color);
    else
      SetAPen(rp, spike2_color);
    if (spk < 13) { /* lower spikes */
      AreaMove(rp,pp->box[0].x,pp->box[1].y);	/* lower left corner	*/
      AreaDraw(rp,pp->box[1].x,pp->box[1].y);	/* lower right corner	*/
      AreaDraw(rp,pp->spike.x,pp->spike.y);	/* point of spike	*/
      AreaDraw(rp,pp->box[0].x,pp->box[1].y);	/* lower left corner	*/
    AreaEnd(rp);
    }
    else { /* upper spikes */
      AreaMove(rp,pp->box[0].x,pp->box[0].y);	/* upper left corner	*/
      AreaDraw(rp,pp->box[1].x,pp->box[0].y);	/* upper right corner	*/
      AreaDraw(rp,pp->spike.x,pp->spike.y);	/* point of spike	*/
      AreaDraw(rp,pp->box[0].x,pp->box[0].y);	/* upper left corner	*/
    AreaEnd(rp);
    }

    if (pieces < 0)
      pieces = -pieces;
    /* go through number of pieces, up to "PerSpike" */
    x = pp->spike.x;
    y = pp->box[1].y;
    if (spk > 12)
      y = pp->box[0].y;
    y += inc / 2;
    inc += 2 * sign(inc);

    for(i = 0; (i < pieces) && (i < PerSpike); i++) {
      PutPiece(x, y, color);
      y += inc;
    }
    /* put some fancy numbers on it if > PerSpike pieces */
    if (pieces > PerSpike) {
      sprintf(line,"%2d",pieces);
      y = pp->box[1].y - 2;
      if (spk > 12)
	y = pp->box[0].y + 8;
      Move(rp, x - screen->Font->ta_YSize, y);
      SetAPen(rp, dice_color);
      Text(rp, line, 2);
    }
  }
}  /* end of PutSpike */

void BlinkPiece(BYTE board[], int pos)
{
  int		x, y, i, color, inc;
  point_rec	*pp;

  if (board[pos]) {		/* Don't bother if nothing there	*/
    pp = &point[pos];
    x = pp->spike.x;
    inc = piece_ptr->height;
    if ((pos == 0) || (pos == 25))
      y = pp->spike.y;
    else {
      inc = -piece_ptr->height;
      y = pp->box[1].y;
      if (pos > 12) {
	inc = -inc;
	y = pp->box[0].y;
      }
      y += inc / 2;
      y += 2 * sign(inc);
    }
    if ((pos != 0) && (pos != 25))
      y += (min(abs(board[pos]), PerSpike) - 1) * inc;
    color = piece2_color;
    if (board[pos] < 0)
      color = piece1_color;
    for(i = 2; i >= 0; i--) {
      Delay(1);
      switch (pos) {
	case  0:
	  PutBarPiece(yc + screen->Font->ta_YSize, board[0], inc, color);
	  break;
	case 25:
	  PutBarPiece(yc - screen->Font->ta_YSize - inc, board[25], inc, color);
	  break;
	default:
	  PutPiece(x, y, dice_color);
      }
      Delay(1);
      switch (pos) {
	case  0:
	  PutBarPiece(yc + screen->Font->ta_YSize, board[0], inc, color);
	  break;
	case 25:
	  PutBarPiece(yc - screen->Font->ta_YSize - inc, board[25], inc, color);
	  break;
	default:
	  PutPiece(x, y, color);
      }
    }
    PutSpike(pos, board[pos]);
  }
}

void TextScreen(char *stuff[], int num)
{
  int			i, n, longest = 0;
  unsigned long		Class;
  struct IOStdReq	*wr;
  struct MsgPort	*wrp;
  struct Window		*crw;
  struct IntuiMessage	*message;
  const char		cons_init[] = "\2330 p";
  struct IntuiText	it = {1, 0, JAM1, 0, 0, NULL, NULL, NULL};

  for (i = 1; i <= num; i++) {
    it.IText = stuff[i];
    if ((n = IntuiTextLength(&it)) > longest)
      longest = n;
  }
  crnw.Width = min(longest +10, screen->Width);
  crnw.LeftEdge = (screen->Width - crnw.Width) / 2;
  crnw.Height  = (num + 1) * screen->Font->ta_YSize;
  crnw.TopEdge = (maxY - crnw.Height) / 2;
  crnw.Screen  = screen;
  crnw.Title   = stuff[0];
  crw          = (struct Window *)OpenWindow(&crnw);
  if (crw != NULL) {
    wrp = CreatePort("my.con.write",0);
    wr = CreateStdIO(wrp);
    wr->io_Data = (APTR) crw;
    wr->io_Length = sizeof(struct Window);
    if (OpenDevice("console.device", 0, (struct IORequest *)wr, 0) == 0) {
      wr->io_Command = CMD_WRITE;
      wr->io_Length = -1;
      wr->io_Data   = (APTR)cons_init;
      DoIO((struct IORequest *)wr);
      /* loop through all the stuff and put it to the open'd console */
      for (i = 1; i < num; i++) {
	wr->io_Length = -1;
	wr->io_Data   = (APTR)stuff[i];
	DoIO((struct IORequest *)wr);
      }
      /* close the console */
      CloseDevice((struct IORequest *)wr);
      DeleteStdIO(wr);
      DeletePort(wrp);
    }/* end-if console created */
    for (;;) {
      Wait(1L << crw->UserPort->mp_SigBit);
      message = (struct IntuiMessage *) GetMsg(crw->UserPort);
      Class = message->Class;
      ReplyMsg((struct Message *)message);
      if (Class == MOUSEBUTTONS)
	break;
    }
    CloseWindow(crw);
  }  /* end-if window opened */
}
