/*
** printed circuit board autorouter, Copyright (C) Randy Nevin 1989.
**
** you may give this software to anyone, make as many copies as you
** like, and post it on public computer bulletin boards and file
** servers. you may not sell it or charge any fee for distribution
** (except for media and postage), remove this comment or the
** copyright notice from the code, or claim that you wrote this code
** or anything derived from it.
**
** the author's address is: Randy Nevin, 1731 211th PL NE, Redmond,
** WA 98053. this code is available directly from the author; just
** send a floppy and a self-addressed floppy mailer with sufficient
** postage. however, you should first attempt to get a copy of this
** software package from the Dr. Dobb's Journal BBS.
*/

/* the low-order bit indicates a hole */
#define HOLE		 0x00000001L /* a conducting hole */

/* traces radiating outward from a hole to a side or corner */
#define HOLE_NORTH	 0x00000002L /* upward             */
#define HOLE_NORTHEAST	 0x00000004L /* upward and right   */
#define HOLE_EAST	 0x00000008L /* to the right       */
#define HOLE_SOUTHEAST	 0x00000010L /* downward and right */
#define HOLE_SOUTH	 0x00000020L /* downward           */
#define HOLE_SOUTHWEST	 0x00000040L /* downward and left  */
#define HOLE_WEST	 0x00000080L /* to the left        */
#define HOLE_NORTHWEST	 0x00000100L /* upward and left    */

/* straight lines through the center */
#define LINE_HORIZONTAL	 0x00000002L /* left-to-right line */
#define LINE_VERTICAL	 0x00000004L /* top-to-bottom line */

/* lines cutting across a corner, connecting adjacent sides */
#define CORNER_NORTHEAST 0x00000008L /* upper right corner */
#define CORNER_SOUTHEAST 0x00000010L /* lower right corner */
#define CORNER_SOUTHWEST 0x00000020L /* lower left corner  */
#define CORNER_NORTHWEST 0x00000040L /* upper left corner  */

/* diagonal lines through the center */
#define DIAG_NEtoSW	 0x00000080L /* northeast to southwest */
#define DIAG_SEtoNW	 0x00000100L /* southeast to northwest */

/* 135 degree angle side-to-far-corner lines */
#define BENT_NtoSE	 0x00000200L /* north to southeast */
#define BENT_NtoSW	 0x00000400L /* north to southwest */
#define BENT_EtoSW	 0x00000800L /* east to southwest  */
#define BENT_EtoNW	 0x00001000L /* east to northwest  */
#define BENT_StoNW	 0x00002000L /* south to northwest */
#define BENT_StoNE	 0x00004000L /* south to northeast */
#define BENT_WtoNE	 0x00008000L /* west to northeast  */
#define BENT_WtoSE	 0x00010000L /* west to southeast  */

/* 90 degree corner-to-adjacent-corner lines */
#define ANGLE_NEtoSE	 0x00020000L /* northeast to southeast */
#define ANGLE_SEtoSW	 0x00040000L /* southeast to southwest */
#define ANGLE_SWtoNW	 0x00080000L /* southwest to northwest */
#define ANGLE_NWtoNE	 0x00100000L /* northwest to northeast */

/* 45 degree angle side-to-near-corner lines */
#define SHARP_NtoNE	 0x00200000L /* north to northeast */
#define SHARP_EtoNE	 0x00400000L /* east to northeast  */
#define SHARP_EtoSE	 0x00800000L /* east to southeast  */
#define SHARP_StoSE	 0x01000000L /* south to southeast */
#define SHARP_StoSW	 0x02000000L /* south to southwest */
#define SHARP_WtoSW	 0x04000000L /* west to southwest  */
#define SHARP_WtoNW	 0x08000000L /* west to northwest  */
#define SHARP_NtoNW	 0x10000000L /* north to northwest */

/* directions the cell can be reached from (point to previous cell) */
#define FROM_NORTH	1
#define FROM_NORTHEAST	2
#define FROM_EAST	3
#define FROM_SOUTHEAST	4
#define FROM_SOUTH	5
#define FROM_SOUTHWEST	6
#define FROM_WEST	7
#define FROM_NORTHWEST	8
#define FROM_OTHERSIDE	9

#define	TOP	0
#define BOTTOM	1
#define EMPTY	0
#define ILLEGAL	-1

/* visit neighboring cells in this order
** (where [9] is on the other side):
**
**	+---+---+---+
**	| 1 | 2 | 3 |
**	+---+---+---+
**	| 4 |[9]| 5 |
**	+---+---+---+
**	| 6 | 7 | 8 |
**	+---+---+---+
*/

static int delta[8][2] = { /* for visiting neighbors on the same side */
	 {  1, -1 }, /* northwest	*/
	 {  1,  0 }, /* north		*/
	 {  1,  1 }, /* northeast	*/
	 {  0, -1 }, /* west		*/
	 {  0,  1 }, /* east		*/
	 { -1, -1 }, /* southwest	*/
	 { -1,  0 }, /* south		*/
	 { -1,  1 }  /* southeast	*/
	};

static int ndir[8] = { /* for building paths back to source */
	 FROM_SOUTHEAST, FROM_SOUTH, FROM_SOUTHWEST,
	 FROM_EAST,                  FROM_WEST,
	 FROM_NORTHEAST, FROM_NORTH, FROM_NORTHWEST
	};

/* blocking masks for neighboring cells */
#define BLOCK_NORTHEAST	( DIAG_NEtoSW | BENT_StoNE | BENT_WtoNE \
			| ANGLE_NEtoSE | ANGLE_NWtoNE \
			| SHARP_NtoNE | SHARP_EtoNE )
#define BLOCK_SOUTHEAST	( DIAG_SEtoNW | BENT_NtoSE | BENT_WtoSE \
			| ANGLE_NEtoSE | ANGLE_SEtoSW \
			| SHARP_EtoSE | SHARP_StoSE )
#define BLOCK_SOUTHWEST	( DIAG_NEtoSW | BENT_NtoSW | BENT_EtoSW \
			| ANGLE_SEtoSW | ANGLE_SWtoNW \
			| SHARP_StoSW | SHARP_WtoSW )
#define BLOCK_NORTHWEST	( DIAG_SEtoNW | BENT_EtoNW | BENT_StoNW \
			| ANGLE_SWtoNW | ANGLE_NWtoNE \
			| SHARP_WtoNW | SHARP_NtoNW )

struct block {
	int r1, c1;  long b1, h1;
	int r2, c2;  long b2, h2;
	};

static struct block blocking[8] = { /* blocking masks */
	 {  0, -1, BLOCK_NORTHEAST, HOLE_NORTHEAST,
	    1,  0, BLOCK_SOUTHWEST, HOLE_SOUTHWEST },
	 {  0,  0, 0, 0, 0, 0, 0, 0 },
	 {  1,  0, BLOCK_SOUTHEAST, HOLE_SOUTHEAST,
	    0,  1, BLOCK_NORTHWEST, HOLE_NORTHWEST },
	 {  0,  0, 0, 0, 0, 0, 0, 0 },
	 {  0,  0, 0, 0, 0, 0, 0, 0 },
	 {  0, -1, BLOCK_SOUTHEAST, HOLE_SOUTHEAST,
	   -1,  0, BLOCK_NORTHWEST, HOLE_NORTHWEST },
	 {  0,  0, 0, 0, 0, 0, 0, 0 },
	 { -1,  0, BLOCK_NORTHEAST, HOLE_NORTHEAST,
	    0,  1, BLOCK_SOUTHWEST, HOLE_SOUTHWEST }
	};

static int selfok[5][8] = { /* mask for self-blocking corner effects */
	 { 1, 1, 1, 1, 1, 1, 1, 1 },
	 { 0, 0, 0, 0, 1, 0, 1, 0 },
	 { 0, 0, 0, 1, 0, 0, 1, 0 },
	 { 0, 1, 0, 0, 1, 0, 0, 0 },
	 { 0, 1, 0, 1, 0, 0, 0, 0 }
	};

static long newmask[5][8] = { /* patterns to mask in neighbor cells */
	 { 0, 0, 0, 0, 0, 0, 0, 0 },
	 { 0, 0, 0, 0, CORNER_NORTHEAST | CORNER_SOUTHEAST, 0,
	   CORNER_SOUTHEAST | CORNER_SOUTHWEST, 0 },
	 { 0, 0, 0, CORNER_SOUTHWEST | CORNER_NORTHWEST, 0, 0,
	   CORNER_SOUTHEAST | CORNER_SOUTHWEST, 0 },
	 { 0, CORNER_NORTHEAST | CORNER_NORTHWEST, 0, 0,
	   CORNER_NORTHEAST | CORNER_SOUTHEAST, 0, 0, 0 },
	 { 0, CORNER_NORTHEAST | CORNER_NORTHWEST, 0,
	   CORNER_SOUTHWEST | CORNER_NORTHWEST, 0, 0, 0, 0 }
	};

/* board dimensions */
extern int Nrows;
extern int Ncols;

void Solve () { /* route all traces */
	int r1, c1, r2, c2, r, c, s, d, a, nr, nc, skip;
	register int i;
	char far *n1;
	char far *n2;
	long curcell, newcell, buddy;
	int newdist, olddir, success, self;

	/* go until no more work to do */
	for (GetWork( &r1, &c1, &n1, &r2, &c2, &n2 ); r1 != ILLEGAL;
			GetWork( &r1, &c1, &n1, &r2, &c2, &n2 )) {
		if (r1 == r2 && c1 == c2) /* already routed */
			continue;
		success = 0;
		InitQueue(); /* initialize the search queue */
		/* get rough estimate of trace distance */
		a = GetApxDist( r1, c1, r2, c2 );
		SetQueue( r1, c1, TOP, 0, a, r2, c2 );
		SetQueue( r1, c1, BOTTOM, 0, a, r2, c2 );
		/* search until success or we exhaust all possibilities */
		for (GetQueue( &r, &c, &s, &d, &a ); r != ILLEGAL;
			GetQueue( &r, &c, &s, &d, &a )) {
			if (r == r2 && c == c2) { /* success! */
				/* lay traces */
				Retrace( r1, c1, r2, c2, s );
				success++;
				break;
				}
			curcell = GetCell( r, c, s );
			if (curcell & CORNER_NORTHWEST)
				self = 1;
			else if (curcell & CORNER_NORTHEAST)
				self = 2;
			else if (curcell & CORNER_SOUTHWEST)
				self = 3;
			else if (curcell & CORNER_SOUTHEAST)
				self = 4;
			else
				self = 0;
			/* consider neighbors */
			for (i = 0; i < 8; i++) {
				/* check self-block */
				if (!selfok[self][i])
					continue;
				if ((nr = r+delta[i][0]) < 0
					|| nr >= Nrows
					|| (nc = c+delta[i][1]) < 0
					|| nc >= Ncols)
					/* off the edge */
					continue;
				newcell = GetCell( nr, nc, s );
				/* check for non-target hole */
				if (newcell & HOLE) {
					if (nr != r2 || nc != c2)
						continue;
					}
				else {
					newcell &= ~(newmask[self][i]);
					/* check for traces */
					if (newcell)
						continue;
					}
				/* check blocking on corner neighbors */
				if (delta[i][0] && delta[i][1]) {
					/* check first buddy */
					buddy = GetCell( r+blocking[i].r1,
						c+blocking[i].c1, s );
					if (buddy & HOLE) {
						if (buddy & (blocking[i].h1))
							continue;
						}
					else if (buddy & (blocking[i].b1))
						continue;
					/* check second buddy */
					buddy = GetCell( r+blocking[i].r2,
						c+blocking[i].c2, s );
					if (buddy & HOLE) {
						if (buddy & (blocking[i].h2))
							continue;
						}
					else if (buddy & (blocking[i].b2))
						continue;
					}
				olddir = GetDir( r, c, s );
				newdist = d+CalcDist( ndir[i], olddir,
					(olddir == FROM_OTHERSIDE)
					? GetDir( r, c, 1-s ) : 0 );
				/* if not visited yet, add it to queue */
				if (!GetDir( nr, nc, s )) {
					SetDir( nr, nc, s, ndir[i] );
					SetDist( nr, nc, s, newdist );
					SetQueue( nr, nc, s, newdist,
						GetApxDist( nr, nc, r2, c2 ),
						r2, c2 );
					}
				/* we might have found a better path */
				else if (newdist < GetDist( nr, nc, s )) {
					SetDir( nr, nc, s, ndir[i] );
					SetDist( nr, nc, s, newdist );
					ReSetQueue( nr, nc, s, newdist,
						GetApxDist( nr, nc, r2, c2 ),
						r2, c2 );
					}
				}
			/* consider other side of board */
			/* check for holes or traces on other side */
			if (newcell = GetCell( r, c, 1-s ))
				continue;
			skip = 0;
			/* check for nearby holes */
			for (i = 0; i < 8; i++) {
				if ((nr = r+delta[i][0]) < 0
					|| nr >= Nrows
					|| (nc = c+delta[i][1]) < 0
					|| nc >= Ncols)
					/* off the edge */
					continue;
				if (GetCell( nr, nc, s ) & HOLE) {
					/* neighboring hole */
					skip = 1;
					break;
					}
				}
			if (skip) /* neighboring hole? */
				continue; /* yes, can't drill one here */
			olddir = GetDir( r, c, s );
			newdist = d+CalcDist( FROM_OTHERSIDE, olddir,
				(olddir == FROM_OTHERSIDE)
				? GetDir( r, c, 1-s ) : 0 );
			/* if not visited yet, add it to queue */
			if (!GetDir( r, c, 1-s )) {
				SetDir( r, c, 1-s, FROM_OTHERSIDE );
				SetDist( r, c, 1-s, newdist );
				SetQueue( r, c, 1-s, newdist,
					a, r2, c2 );
				}
			/* we might have found a better path */
			else if (newdist < GetDist( r, c, 1-s )) {
				SetDir( r, c, 1-s, FROM_OTHERSIDE );
				SetDist( r, c, 1-s, newdist );
				ReSetQueue( r, c, 1-s, newdist,
					a, r2, c2 );
				}
			}
		if (!success)
			printf( "\t*!* UNSUCCESSFUL *!*\n" );
		/* clear direction flags */
		for (r = 0; r < Nrows; r++) {
			for (c = 0; c < Ncols; c++) {
				SetDir( r, c, TOP, EMPTY );
				SetDir( r, c, BOTTOM, EMPTY );
				}
			}
		}
	}

/* this table drives the retracing phase */
static long bit[8][9] = { /* OT=Otherside */
	 /* N, NE, E, SE, S, SW, W, NW, OT */
/* N */  { LINE_VERTICAL, BENT_StoNE, CORNER_SOUTHEAST, SHARP_StoSE,
	   0, SHARP_StoSW, CORNER_SOUTHWEST, BENT_StoNW,
	   (HOLE | HOLE_SOUTH) },
/* NE */ { BENT_NtoSW, DIAG_NEtoSW, BENT_EtoSW, ANGLE_SEtoSW,
	   SHARP_StoSW, 0, SHARP_WtoSW, ANGLE_SWtoNW,
	   (HOLE | HOLE_SOUTHWEST) },
/* E */  { CORNER_NORTHWEST, BENT_WtoNE, LINE_HORIZONTAL, BENT_WtoSE,
	   CORNER_SOUTHWEST, SHARP_WtoSW, 0, SHARP_WtoNW,
	   (HOLE | HOLE_WEST) },
/* SE */ { SHARP_NtoNW, ANGLE_NWtoNE, BENT_EtoNW, DIAG_SEtoNW,
	   BENT_StoNW, ANGLE_SWtoNW, SHARP_WtoNW, 0,
	   (HOLE | HOLE_NORTHWEST) },
/* S */  { 0, SHARP_NtoNE, CORNER_NORTHEAST, BENT_NtoSE,
	   LINE_VERTICAL, BENT_NtoSW, CORNER_NORTHWEST, SHARP_NtoNW,
	   (HOLE | HOLE_NORTH) },
/* SW */ { SHARP_NtoNE, 0, SHARP_EtoNE, ANGLE_NEtoSE, BENT_StoNE,
	   DIAG_NEtoSW, BENT_WtoNE, ANGLE_NWtoNE,
	   (HOLE | HOLE_NORTHEAST) },
/* W */  { CORNER_NORTHEAST, SHARP_EtoNE, 0, SHARP_EtoSE,
	   CORNER_SOUTHEAST, BENT_EtoSW, LINE_HORIZONTAL, BENT_EtoNW,
	   (HOLE | HOLE_EAST) },
/* NW */ { BENT_NtoSE, ANGLE_NEtoSE, SHARP_EtoSE, 0, SHARP_StoSE,
           ANGLE_SEtoSW, BENT_WtoSE, DIAG_SEtoNW,
	   (HOLE | HOLE_SOUTHEAST) }
	};

void Retrace ( rr1, cc1, rr2, cc2, s )
	/* work from target back to source, laying down the trace */
	int rr1, cc1, rr2, cc2, s; /* start on side s */
	{
	int r0, c0, s0, r1, c1, s1, r2, c2, s2;
	register int x, y;
	long b;

	r1 = rr2;
	c1 = cc2;
	s1 = s;
	r0 = c0 = s0 = ILLEGAL;
	do {
		/* find where we came from to get here */
		switch (x = GetDir( r2 = r1, c2 = c1, s2 = s1 )) {
		case FROM_NORTH:	r2++;		break;
		case FROM_EAST:			c2++;	break;
		case FROM_SOUTH:	r2--;		break;
		case FROM_WEST:			c2--;	break;
		case FROM_NORTHEAST:	r2++;	c2++;	break;
		case FROM_SOUTHEAST:	r2--;	c2++;	break;
		case FROM_SOUTHWEST:	r2--;	c2--;	break;
		case FROM_NORTHWEST:	r2++;	c2--;	break;
		case FROM_OTHERSIDE:	s2 = 1-s2;	break;
		default:
			fprintf( stderr, "internal error\n" );
			exit( -1 );
			break;
			}
		if (r0 != ILLEGAL)
			y = GetDir( r0, c0, s0 );
		/* see if target or hole */
		if ((r1 == rr2 && c1 == cc2) || (s1 != s0)) {
			switch (x) {
			case FROM_NORTH:
			  OrCell( r1, c1, s1, HOLE_NORTH );  break;
			case FROM_EAST:
			  OrCell( r1, c1, s1, HOLE_EAST );  break;
			case FROM_SOUTH:
			  OrCell( r1, c1, s1, HOLE_SOUTH );  break;
			case FROM_WEST:
			  OrCell( r1, c1, s1, HOLE_WEST );  break;
			case FROM_NORTHEAST:
			  OrCell( r1, c1, s1, HOLE_NORTHEAST );  break;
			case FROM_SOUTHEAST:
			  OrCell( r1, c1, s1, HOLE_SOUTHEAST );  break;
			case FROM_SOUTHWEST:
			  OrCell( r1, c1, s1, HOLE_SOUTHWEST );  break;
			case FROM_NORTHWEST:
			  OrCell( r1, c1, s1, HOLE_NORTHWEST );  break;
			case FROM_OTHERSIDE:
			default:
				fprintf( stderr, "internal error\n" );
				exit( -1 );
				break;
				}
			}
		else {
			if ((y == FROM_NORTH
				|| y == FROM_NORTHEAST
				|| y == FROM_EAST
				|| y == FROM_SOUTHEAST
				|| y == FROM_SOUTH
				|| y == FROM_SOUTHWEST
				|| y == FROM_WEST
				|| y == FROM_NORTHWEST)
				&& (x == FROM_NORTH
				|| x == FROM_NORTHEAST
				|| x == FROM_EAST
				|| x == FROM_SOUTHEAST
				|| x == FROM_SOUTH
				|| x == FROM_SOUTHWEST
				|| x == FROM_WEST
				|| x == FROM_NORTHWEST
				|| x == FROM_OTHERSIDE)
				&& (b = bit[y-1][x-1])) {
				OrCell( r1, c1, s1, b );
				if (b & HOLE)
					OrCell( r2, c2, s2, HOLE );
				}
			else {
				fprintf( stderr, "internal error\n" );
				exit( -1 );
				}
			}
		if (r2 == rr1 && c2 == cc1) { /* see if source */
			switch (x) {
			case FROM_NORTH:
			  OrCell( r2, c2, s2, HOLE_SOUTH );  break;
			case FROM_EAST:
			  OrCell( r2, c2, s2, HOLE_WEST );  break;
			case FROM_SOUTH:
			  OrCell( r2, c2, s2, HOLE_NORTH );  break;
			case FROM_WEST:
			  OrCell( r2, c2, s2, HOLE_EAST );  break;
			case FROM_NORTHEAST:
			  OrCell( r2, c2, s2, HOLE_SOUTHWEST );  break;
			case FROM_SOUTHEAST:
			  OrCell( r2, c2, s2, HOLE_NORTHWEST );  break;
			case FROM_SOUTHWEST:
			  OrCell( r2, c2, s2, HOLE_NORTHEAST );  break;
			case FROM_NORTHWEST:
			  OrCell( r2, c2, s2, HOLE_SOUTHEAST );  break;
			case FROM_OTHERSIDE:
			default:
				fprintf( stderr, "internal error\n" );
				exit( -1 );
				break;
				}
			}
		/* move to next cell */
		r0 = r1; c0 = c1; s0 = s1;
		r1 = r2; c1 = c2; s1 = s2;
		} while (!(r2 == rr1 && c2 == cc1));
	}

int GetApxDist ( r1, c1, r2, c2 ) /* calculate approximate distance */
	int r1, c1, r2, c2;
	{
	register int d1, d2; /* row and column deltas */
	int d0; /* temporary variable for swapping d1 and d2 */

	/* NOTE: the -25 used below is because we are not going */
	/* from the center of (r1,c1) to the center of (r2,c2), */
	/* we are going from the edge of a hole at (r1,c1) to   */
	/* the edge of a hole at (r2,c2). holes are 25 mils in  */
	/* diameter (12.5 mils in radius), so we back off by 2  */
	/* radii.                                               */
	if ((d1 = r1-r2) < 0) /* get absolute row delta */
		d1 = -d1;
	if ((d2 = c1-c2) < 0) /* get absolute column delta */
		d2 = -d2;
	if (!d1) /* in same row? */
		return( (d2*50)-25 ); /* 50 mils per cell */
	if (!d2) /* in same column? */
		return( (d1*50)-25 ); /* 50 mils per cell */
	if (d1 > d2) { /* get smaller into d1 */
		d0 = d1;
		d1 = d2;
		d2 = d0;
		}
	d2 -= d1; /* get non-diagonal part of approximate route */
	return( (d1*71)+(d2*50)-25 ); /* 71 mils diagonally per cell */
	}

/* distance to go through a cell */
static int dist[10][10] = { /* OT=Otherside, OR=Origin (source) cell */
	 /* N, NE,  E, SE,  S, SW,  W, NW,   OT, OR */
/* N  */ { 50, 60, 35, 60, 99, 60, 35, 60,   12, 12 },
/* NE */ { 60, 71, 60, 71, 60, 99, 60, 71,   23, 23 },
/* E  */ { 35, 60, 50, 60, 35, 60, 99, 60,   12, 12 },
/* SE */ { 60, 71, 60, 71, 60, 71, 60, 99,   23, 23 },
/* S  */ { 99, 60, 35, 60, 50, 60, 35, 60,   12, 12 },
/* SW */ { 60, 99, 60, 71, 60, 71, 60, 71,   23, 23 },
/* W  */ { 35, 60, 99, 60, 35, 60, 50, 60,   12, 12 },
/* NW */ { 60, 71, 60, 99, 60, 71, 60, 71,   23, 23 },

/* OT */ { 12, 23, 12, 23, 12, 23, 12, 23,   99, 99 },
/* OR */ { 99, 99, 99, 99, 99, 99, 99, 99,   99, 99 }
	};

/* distance around (circular) segment of hole */
static int circ[10][10] = { /* OT=Otherside, OR=Origin (source) cell */
	 /* N, NE,  E, SE,  S, SW,  W, NW,   OT, OR */
/* N  */ { 39, 29, 20, 10,  0, 10, 20, 29,   99, 0 },
/* NE */ { 29, 39, 29, 20, 10,  0, 10, 20,   99, 0 },
/* E  */ { 20, 29, 39, 29, 20, 10,  0, 10,   99, 0 },
/* SE */ { 10, 20, 29, 39, 29, 20, 10,  0,   99, 0 },
/* S  */ {  0, 10, 20, 29, 39, 29, 20, 10,   99, 0 },
/* SW */ { 10,  0, 10, 20, 29, 39, 29, 20,   99, 0 },
/* W  */ { 20, 10,  0, 10, 20, 29, 39, 29,   99, 0 },
/* NW */ { 29, 20, 10,  0, 10, 20, 29, 39,   99, 0 },

/* OT */ { 99, 99, 99, 99, 99, 99, 99, 99,   99, 0 },
/* OR */ { 99, 99, 99, 99, 99, 99, 99, 99,   99, 0 }
	};

/* penalty for routing holes and turns, scaled by sharpness of turn */
static int penalty[10][10] = { /* OT=Otherside, OR=Origin (source) cell */
	 /* N, NE,  E, SE,  S, SW,  W, NW,   OT, OR */
/* N  */ {  0,  5, 10, 15, 20, 15, 10,  5,   50, 0 },
/* NE */ {  5,  0,  5, 10, 15, 20, 15, 10,   50, 0 },
/* E  */ { 10,  5,  0,  5, 10, 15, 20, 15,   50, 0 },
/* SE */ { 15, 10,  5,  0,  5, 10, 15, 20,   50, 0 },
/* S  */ { 20, 15, 10,  5,  0,  5, 10, 15,   50, 0 },
/* SW */ { 15, 20, 15, 10,  5,  0,  5, 10,   50, 0 },
/* W  */ { 10, 15, 20, 15, 10,  5,  0,  5,   50, 0 },
/* NW */ {  5, 10, 15, 20, 15, 10,  5,  0,   50, 0 },

/* OT */ { 50, 50, 50, 50, 50, 50, 50, 50,  100, 0 },
/* OR */ {  0,  0,  0,  0,  0,  0,  0,  0,    0, 0 }
	};

/*
** x is the direction to enter the cell of interest.
** y is the direction to exit the cell of interest.
** z is the direction to really exit the cell, if y=FROM_OTHERSIDE.
**
** return the distance of the trace through the cell of interest.
** the calculation is driven by the tables above.
*/

int CalcDist ( x, y, z )
	/* calculate distance of a trace through a cell */
	int x, y, z;
	{
	int adjust;

	adjust = 0; /* set if hole is encountered */
	if (x == EMPTY)
		x = 10;
	if (y == EMPTY)
		y = 10;
	else if (y == FROM_OTHERSIDE) {
		if (z == EMPTY)
			z = 10;
		adjust = circ[x-1][z-1] + penalty[x-1][z-1];
		}
	return( dist[x-1][y-1] + penalty[x-1][y-1] + adjust );
	}
