From: sater@sater.home.cs.vu.nl (Hans van Staveren)
Subject: dealer program source, second release
Organization: The home machine of Hans van Staveren
Date: Sun, 15 Nov 1992 22:09:00 GMT

The dealer program I posted has released moderately positive response.
One bug was detected, and there were some suggestions for improvement.
Here is the second release.

Bugs fixed:
	the shape() call returned integers not equal to 1 when true,
	this screwed up things when used in a context not boolean
Extra features:
- It is now possible to predeal certain cards to certain players,
  and distribute the rest at random.
- The shape() operator now has better possibilities to include and exclude
  shapes. Like any 54 in the majors but no void is now trivial to express.

Thanks to the people that made suggestions.

	Hans van Staveren
	Amsterdam, Holland

: This is a shar archive.  Extract with sh, not csh.
: This archive ends with exit, so do not worry about trailing junk.
echo 'Extracting README'
sed 's/^X//' > README << '+ END-OF-FILE README'
XThis program is hereby put in the public domain. Do with it whatever
Xyou want, but I would like you not to redistribute it in modified form
Xwithout mentioning the fact of modification. I will accept bug reports
Xand modification requests, without any obligation of course, but fixing
Xbugs someone else put in is beyond me.
X
XWhen you report bugs please mention the version number in the source
Xfiles, and preferably send context diffs if you changed anything.
XI might put in your fixes, and distribute a new version someday.
X
XI would prefer if you did *not* use this program for generating hands
Xfor tournaments. I have not investigated the random number generation
Xclosely enough for me to be comfortable with that thought.
X
X	Hans van Staveren
X	Amsterdam
X	Holland
X	<sater@sater.home.cs.vu.nl>
+ END-OF-FILE README
chmod 'u=r,g=r,o=r' 'README'
echo 'SENT: -r--r--r--  1 sater         792 Nov  4 20:52 README'
echo -n 'RCVD: '
/bin/ls -l README
echo 'Extracting Description'
sed 's/^X//' > Description << '+ END-OF-FILE Description'
X	DEALER, a bridge hand generator program
X	Hans van Staveren <sater@sater.home.cs.vu.nl>
X
XThe program dealer can be used to generate hands for partnerships
Xbidding training or for generating statistics that can be used to
Xdesign conventions, or win postmortems.
X
XThe program reads the description from standard input, and writes
Xresults on standard output.
X
XIn general the input specifies a condition and an action, and the
Xprogram will generate a lot of hands, and if the condition is satisfied
Xexecute the action.
X
XA simple example:
X
X	shape(north, any 4333 + any 4423) and hcp(north)>=19
X
Xas input will print 40 hands with strong balanced Norths.
X
XHere is a partial explanation of all possible inputs: if you really want
Xto know all read the source.
X
Xgenerate <number>
X	generate <number> hands. If you do not specify this a million
X	will be generated
Xproduce <number>
X	produce <number> hands that satisfy the condition. If you do specify
X	this and you want the hands to be printed you get 40. If you only
X	asked for statistics or so you will get a hundred thousand.
X<identifier> = <expression>
X	defines <identifier> to represent <expression>
Xpredeal <predeallist>
X	can be used to assign certain cards to certain hands, for example
X	to check probabilities for actual play. See the example in
X	Descr.6c for enlightenment.
Xcondition <expression>
X	for every hand generated <expression> is calculated. If it is
X	non zero the corresponding action is executed. If you do not
X	specify a condition the constant 1 will be assumed.
X	The word condition can actually be omitted.
Xaction <actionlist>
X	list of actions to be executed. If you do not specify an action
X	the "printall" action is assumed.
X
XThe "generate" and "produce" numbers are upper limits. The first one
Xto become true will terminate the program.
X
XAn expression looks like a C expression, with all normal operators
Xpresent. If you look for leftshift, or exponentiation forget it,
Xbut all normal ones are there, with their normal priorities.
XParentheses can be used, and all in all it is just like C.
XThe && form can be used, or the word "and". The same goes for "or"
Xand "not".
X
XThe special operators needed in a bridge-dealer program are provided:
X
X<suit> ( <compass> ) , eg hearts(west)
X	the number of cards in the suit held by the player
Xhcp ( <compass> ), eg hcp(north)
X	the number of high card points held by the player, 4321 count.
Xhcp ( <compass>, <suit> ), eg hcp(south, spades)
X	the number of high card points in the specified suit
Xhascard ( <compass>, <card> ), eg hascard(east, TC)
X	whether east holds the 10 (T) of clubs
Xshape ( <compass>, shapelist),
X		eg shape(north, any 4333 + 54xx - any 0xxx)
X	whether north holds one of the shapes in the list,
X	in this case either a 4333 in any suit, or 5 spades and 4 hearts,
X	but no void anywhere.
X	A shapelist is a list of shapes combined with + or - signs.
X	The word any prepended to a shape means that any suit can match
X	the numbers, without the word any the shapes are the normal ones
X	in the order spades, hearts, diamonds and clubs.
X	Instead of a digit the letter 'x' can be given to match any length.
X	All of this means that "any 55xx" means any shape with two five
X	card suits.
X	This operator is one of the most important in the program and is
X	very efficiently implemented. Any shape() call, no matter how
X	complicated is executed in constant time. Use shape()
X	for all length expressions if you can.
X
XThe different actions are:
Xprintall
X	prints all four hands next to each other. This is the default.
Xprint (<list of compasses>), eg print(east,west)
X	print all hands specified on separate pages. This is the best
X	way to generate hands to be used for partnership training.
X	One of the partners gets one page, and one the other and they can
X	start practicing.
Xaverage "optional string" <expr>, eg	average "points" hcp(north)
X	calculates and prints the average of the expression over all
X	hands satisfying the condition. The optional strings is printed
X	before the average.
Xfrequency "optional string" ( <expr>, <lowbnd>, <highbnd> )
X	calculates and prints a histogram of the values of <expr>,
X	between the bounds <lowbnd> and <highbnd> inclusive.
X	The optional string is printed before it.
X
XThe speed of the program obviously depends on the machine it runs on.
XOn my home machine, a SparcStation 1+, it generates more than 3000 hands
Xa second if the condition is not too complicated. I rarely play more than
X150 hands a week, so in about two seconds I can generate the boards of a year.
X
XIt satisfies me.
X
XPortability:
XThe program has been developed under SunOS on Sun hardware. I am a reasonably
Xcompetent programmer though, and I expect the program to be highly portable.
XThe most suspect part is in the initialisation of the random number generator.
XThe system call used here might need to be changed on other OSes.
+ END-OF-FILE Description
chmod 'u=r,g=r,o=r' 'Description'
echo 'SENT: -r--r--r--  1 sater        4836 Nov 15 23:00 Description'
echo -n 'RCVD: '
/bin/ls -l Description
echo 'Extracting Makefile'
sed 's/^X//' > Makefile << '+ END-OF-FILE Makefile'
X# $Header: /home/sater/bridge/dealer/RCS/Makefile,v 1.2 1992/11/15 22:00:48 sater Exp $
XCFLAGS=-O -DNDEBUG
X
Xdealer: dealer.o defs.o
X	$(CC) $(CFLAGS) -o dealer dealer.o defs.o -ll
X
Xlint: dealer.c defs.c
X	lint dealer.c defs.c
X
Xclean:
X	rm -f defs.c scan.c *.o dealer
X
Xsharf: README Description Makefile dealer.c defs.y scan.l tree.h
X	shar README Description Makefile dealer.c defs.y scan.l tree.h Descr.* >sharf
X
Xdealer.o: tree.h
Xdefs.o:	scan.c tree.h
+ END-OF-FILE Makefile
chmod 'u=r,g=r,o=r' 'Makefile'
echo 'SENT: -r--r--r--  1 sater         449 Nov 15 23:00 Makefile'
echo -n 'RCVD: '
/bin/ls -l Makefile
echo 'Extracting dealer.c'
sed 's/^X//' > dealer.c << '+ END-OF-FILE dealer.c'
X#include <stdio.h>
X#include <sys/time.h>
X#include "tree.h"
X#include <assert.h>
X
Xstatic char rcsid[] = "$Header: /home/sater/bridge/dealer/RCS/dealer.c,v 1.3 1992/11/15 21:26:08 sater Exp $";
X
Xtypedef unsigned char card;
X
Xchar *player_name[] = { "North", "East", "South", "West" };
Xchar representation[] = "23456789TJQKA";
X
Xint points[13] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4 };
Xtypedef card deal[52];
X
X/*
X * Various handshapes can be asked for. For every shape the user is
X * interested in a number is generated. In every distribution that fits that
X * shape the corresponding bit is set in the distrbitmaps 4-dimensional array.
X * This makes looking up a shape a small constant cost.
X */
X#define MAXDISTR	8*sizeof(int)
Xint ***distrbitmaps[14];
X
Xstruct handstat {
X	int	hs_length[NSUITS];	/* distribution */
X	int	hs_points[NSUITS];	/* 4321 HCP per suit */
X	int	hs_totalpoints;		/* Sum of above four */
X	int	hs_bits;		/* Bitmap to check distribution */
X};
X
Xint will_print;		/* Is there a print action? */
X
Xpraction() {
X
X	if (will_print++)
X		yyerror("Only one printaction maximum");
X}
X
Xchar * mycalloc(nel, siz)
Xunsigned nel, siz;
X{
X	char *p, *calloc();
X
X	p = calloc(nel, siz);
X	if (p)
X		return p;
X	fprintf(stderr, "Out of memory\n");
X	exit(-1);
X	/*NOTREACHED*/
X}
X
Xinitdistr() {
X    int ***p4,**p3,*p2;
X    int clubs, diamonds, hearts;
X	
X    /*
X     * Allocate the four dimensional pointer array
X     */
X
X    for(clubs=0;clubs<=13;clubs++) {
X	p4 = (int ***) mycalloc((unsigned)14-clubs, sizeof(*p4));
X	distrbitmaps[clubs] = p4;
X	for(diamonds=0;diamonds<=13-clubs;diamonds++) {
X	    p3 = (int **) mycalloc((unsigned)14-clubs-diamonds, sizeof(*p3));
X	    p4[diamonds] = p3;
X	    for(hearts=0;hearts<=13-clubs-diamonds;hearts++) {
X		p2 = (int *) mycalloc((unsigned)14-clubs-diamonds-hearts, sizeof(*p2));
X		p3[hearts] = p2;
X	    }
X	}
X    }
X}
X
Xsetshapebit(cl, di, ht, sp, msk, excepted)
X{
X
X	if (excepted)
X		distrbitmaps[cl][di][ht][sp] &= ~msk;
X	else
X		distrbitmaps[cl][di][ht][sp] |= msk;
X}
X
Xnewpack(d)
Xdeal d;
X{
X	int suit, rank, place;
X
X	place = 0;
X	for (suit=SUIT_CLUB; suit <= SUIT_SPADE; suit++)
X		for(rank=0; rank<13; rank++)
X			d[place++] = MAKECARD(suit, rank);
X}
X
Xemptypack(d)
Xdeal d;
X{
X	int i;
X
X	for (i=0; i<52; i++)
X		d[i] = NO_CARD;
X}
X
Xhascard(d, player, onecard)
Xdeal d;
Xint player;
Xcard onecard;
X{
X	int i;
X
X	for(i=player*13; i<(player+1)*13; i++)
X		if (d[i] == onecard)
X			return 1;
X	return 0;
X}
X
Xmake_card(rankchar, suitchar)
Xchar rankchar, suitchar;
X{
X	int rank, suit;
X
X	for (rank=0;rank<13 && representation[rank]!=rankchar;rank++)
X		;
X	assert(rank<13);
X	switch(suitchar) {
X	case 'C':	suit = 0; break;
X	case 'D':	suit = 1; break;
X	case 'H':	suit = 2; break;
X	case 'S':	suit = 3; break;
X	default:	assert(0);
X	}
X	return MAKECARD(suit, rank);
X}
X
Xint use_compass[NSUITS];
X
Xanalyze(d, hs)
Xdeal d;
Xstruct handstat *hs;
X{
X	int player,base,next,c,r,s;
X	card curcard;
X
X	for (player = COMPASS_NORTH, base = 0; player <= COMPASS_WEST;
X						player++, hs++, base += 13) {
X		/*
X		 * If the expressions in the input never mention a player
X		 * we do not calculate his hand statistics.
X		 */
X		if (use_compass[player]==0)
X			continue;
X		next = base;
X		for (s=SUIT_CLUB; s<=SUIT_SPADE; s++) {
X			hs->hs_length[s] = 0;
X			hs->hs_points[s] = 0;
X		}
X		for (c=0; c<13; c++) {
X			curcard = d[next++];
X			s = C_SUIT(curcard);
X			r = C_RANK(curcard);
X			hs->hs_length[s]++;
X			hs->hs_points[s] += points[r];
X		}
X		hs->hs_totalpoints = 0;
X		for (s=SUIT_CLUB; s<=SUIT_SPADE; s++)
X			hs->hs_totalpoints += hs->hs_points[s];
X		hs->hs_bits = distrbitmaps
X				[hs->hs_length[SUIT_CLUB]]
X				[hs->hs_length[SUIT_DIAMOND]]
X				[hs->hs_length[SUIT_HEART]]
X				[hs->hs_length[SUIT_SPADE]];
X	}
X}
X
X
Xprintdeal(d)
Xdeal d;
X{
X	int suit,player,rank,cards;;
X
X	for( suit=SUIT_SPADE; suit>= SUIT_CLUB; suit--) {
X		cards=10;
X		for (player=COMPASS_NORTH; player<=COMPASS_WEST; player++) {
X			while (cards<10) {
X				printf("  ");
X				cards++;
X			}
X			cards=0;
X			for (rank = 12; rank >=0; rank--) {
X				if (hascard(d, player, MAKECARD(suit, rank))) {
X					printf("%c ", representation[rank]);
X					cards++;
X				}
X			}
X			if (cards == 0) {
X				printf("- ");
X				cards++;
X			}
X		}
X		printf("\n");
X	}
X	printf("\n");
X}
X
Xint nprod,maxproduce;
Xint ngen,maxgenerate;
Xstruct tree defaulttree = {TRT_NUMBER, NIL, NIL, 1, 0};
Xstruct tree *decisiontree = &defaulttree;
Xstruct action defaultaction = { (struct action *) 0, ACT_PRINTALL };
Xstruct action *actionlist = &defaultaction;
X
Xdeal fullpack;
Xdeal stacked_pack;
Xdeal curdeal;
X
Xstruct handstat hs[4];
X
Xsetup_deal() {
X	register i,j;
X
X	j = 0;
X	for (i=0; i<52; i++) {
X		if (stacked_pack[i] != NO_CARD) {
X			curdeal[i] = stacked_pack[i];
X		} else {
X			while (fullpack[j] == NO_CARD)
X				j++;
X			curdeal[i] = fullpack[j++];
X			assert(j <= 52);
X		}
X	}
X}
X
Xpredeal(player, onecard)
Xint player;
Xcard onecard;
X{
X	int i,j;
X
X	for(i=0; i<52; i++) {
X		if (fullpack[i] == onecard) {
X			fullpack[i] = NO_CARD;
X			for(j=player*13; j<(player+1)*13; j++)
X				if (stacked_pack[j] == NO_CARD) {
X					stacked_pack[j] = onecard;
X					return;
X				}
X			yyerror("More than 13 cards for one player");
X		}
X	}
X	yyerror("Card predealt twice");
X}
X
X
X#define RANDBITS 16
X#define NRANDVALS (1<<RANDBITS)
X
Xunsigned char zero52[NRANDVALS];
X
Xinitrandom() {
X	struct timeval tv;
X	int i, i_cycle;
X	int val;
X
X	/*
X	 * The most suspect part of this program
X	 */
X	gettimeofday(&tv, (void *) 0);
X	srandom(tv.tv_sec^tv.tv_usec);
X	/*
X	 * End of suspect part
X	 *
X	 * Now initialize array zero52 with numbers 0..51
X	 * repeatedly. This whole charade is just to prevent having
X	 * to do divisions.
X	 */
X	val = 0;
X	for (i=0, i_cycle=0; i< NRANDVALS; i++) {
X		while (stacked_pack[val] != NO_CARD) {
X			/* this slot is predealt, do not use it */
X			val++;
X			if (val==52) {
X				val=0;
X				i_cycle = i;
X			}
X		}
X		zero52[i] = val++;
X		if (val==52) {
X			val=0;
X			i_cycle = i+1;
X		}
X	}
X	/*
X	 * Fill the last part of the array with 0xFF, just to prevent
X	 * that 0 occurs more than 51. This is probably just for hack value
X	 */
X	while (i > i_cycle) {
X		zero52[i-1] = 0xFF;
X		i--;
X	}
X}
X
Xshuffle(d)
Xdeal d;
X{
X	int i,j;
X	card t;
X
X	/*
X	 * Algorithm according to Knuth. For each card exchange with
X	 * a random other card. This is supposed to be the perfect
X	 * shuffle algorithm. It only depends on a valid random number
X	 * generator.
X	 */
X	for (i=0; i<52; i++) {
X		if (stacked_pack[i] == NO_CARD) {
X			do
X				/* Upper bits most random */
X				j = zero52[random()>>(31-RANDBITS)];
X			while (j==0xFF);
X			t = d[j]; d[j] = d[i]; d[i] = t;
X		}
X	}
X}
X
Xevaltree(t)
Xstruct tree *t;
X{
X
X	switch(t->tr_type) {
X	default:
X		assert(0);
X	case TRT_NUMBER:
X		return t->tr_int1;
X	case TRT_AND2:
X		return evaltree(t->tr_leaf1) && evaltree(t->tr_leaf2);
X	case TRT_OR2:
X		return evaltree(t->tr_leaf1) || evaltree(t->tr_leaf2);
X	case TRT_ARPLUS:
X		return evaltree(t->tr_leaf1) +  evaltree(t->tr_leaf2);
X	case TRT_ARMINUS:
X		return evaltree(t->tr_leaf1) -  evaltree(t->tr_leaf2);
X	case TRT_ARTIMES:
X		return evaltree(t->tr_leaf1) *  evaltree(t->tr_leaf2);
X	case TRT_ARDIVIDE:
X		return evaltree(t->tr_leaf1) /  evaltree(t->tr_leaf2);
X	case TRT_ARMOD:
X		return evaltree(t->tr_leaf1) %  evaltree(t->tr_leaf2);
X	case TRT_CMPEQ:
X		return evaltree(t->tr_leaf1) == evaltree(t->tr_leaf2);
X	case TRT_CMPNE:
X		return evaltree(t->tr_leaf1) != evaltree(t->tr_leaf2);
X	case TRT_CMPLT:
X		return evaltree(t->tr_leaf1) <  evaltree(t->tr_leaf2);
X	case TRT_CMPLE:
X		return evaltree(t->tr_leaf1) <= evaltree(t->tr_leaf2);
X	case TRT_CMPGT:
X		return evaltree(t->tr_leaf1) >  evaltree(t->tr_leaf2);
X	case TRT_CMPGE:
X		return evaltree(t->tr_leaf1) >= evaltree(t->tr_leaf2);
X	case TRT_NOT:
X		return !evaltree(t->tr_leaf1);
X	case TRT_LENGTH:	/* suit, compass */
X		assert(t->tr_int1>=SUIT_CLUB && t->tr_int1<=SUIT_SPADE);
X		assert(t->tr_int2>=COMPASS_NORTH && t->tr_int2<=COMPASS_WEST);
X		return hs[t->tr_int2].hs_length[t->tr_int1];
X	case TRT_HCPTOTAL:	/* compass */
X		assert(t->tr_int1>=COMPASS_NORTH && t->tr_int1<=COMPASS_WEST);
X		return hs[t->tr_int1].hs_totalpoints;
X	case TRT_HCP:		/* compass, suit */
X		assert(t->tr_int1>=COMPASS_NORTH && t->tr_int1<=COMPASS_WEST);
X		assert(t->tr_int2>=SUIT_CLUB && t->tr_int2<=SUIT_SPADE);
X		return hs[t->tr_int1].hs_points[t->tr_int2];
X	case TRT_SHAPE:		/* compass, shapemask */
X		assert(t->tr_int1>=COMPASS_NORTH && t->tr_int1<=COMPASS_WEST);
X		assert(t->tr_int2>=0 && t->tr_int2<MAXDISTR);
X		return (hs[t->tr_int1].hs_bits&t->tr_int2) != 0;
X	case TRT_HASCARD:	/* compass, card */
X		assert(t->tr_int1>=COMPASS_NORTH && t->tr_int1<=COMPASS_WEST);
X		return hascard(curdeal, t->tr_int1, t->tr_int2);
X	}
X}
X	
Xinteresting()
X{
X	return evaltree(decisiontree);
X}
X
Xdeal *deallist;
X
Xsetup_action() {
X    struct action *acp;
X		
X    /*
X     * Initialize all actions
X     */
X    for (acp=actionlist; acp!=0; acp = acp->ac_next) {
X	switch(acp->ac_type) {
X	default:
X	    assert(0);
X	    /*NOTREACHED*/
X	case ACT_PRINTALL:
X	    break;
X	case ACT_PRINT:
X	    deallist = (deal *) mycalloc(maxproduce, sizeof(deal));
X	    break;
X	case ACT_AVERAGE:
X	    break;
X	case ACT_FREQUENCY:
X	    acp->ac_u.acu_f.acuf_freqs = (long *) mycalloc(
X		acp->ac_u.acu_f.acuf_highbnd - acp->ac_u.acu_f.acuf_lowbnd + 1,
X							sizeof(long));
X	    break;
X	}
X    }
X}
X
Xaction() {
X    struct action *acp;
X    int expr;
X
X    for (acp=actionlist; acp!=0; acp = acp->ac_next) {
X	switch(acp->ac_type) {
X	default:
X	    assert(0);
X	    /*NOTREACHED*/
X	case ACT_PRINTALL:
X	    printdeal(curdeal);
X	    break;
X	case ACT_PRINT:
X	    bcopy(curdeal, deallist[nprod], sizeof(deal));
X	    break;
X	case ACT_AVERAGE:
X	    acp->ac_int1 += evaltree(acp->ac_expr1);
X	    break;
X	case ACT_FREQUENCY:
X	    expr = evaltree(acp->ac_expr1);
X	    if (expr < acp->ac_u.acu_f.acuf_lowbnd)
X		acp->ac_u.acu_f.acuf_uflow++;
X	    else if (expr > acp->ac_u.acu_f.acuf_highbnd)
X		acp->ac_u.acu_f.acuf_oflow++;
X	    else
X		acp->ac_u.acu_f.acuf_freqs[expr-acp->ac_u.acu_f.acuf_lowbnd]++;
X	    break;
X		
X	}
X    }
X}
X
Xprinthands(boardno, dealp, player, nhands)
Xint boardno, player, nhands;
Xdeal *dealp;
X{
X	int i, suit,rank,cards;;
X
X	for(i=0; i<nhands; i++)
X		printf("%4d.%15c", boardno+i+1, ' ');
X	printf("\n");
X	for( suit=SUIT_SPADE; suit>= SUIT_CLUB; suit--) {
X		cards=10;
X		for (i=0; i<nhands; i++) {
X			while (cards<10) {
X				printf("  ");
X				cards++;
X			}
X			cards=0;
X			for (rank = 12; rank >=0; rank--) {
X				if (hascard(dealp[i], player, MAKECARD(suit, rank))) {
X					printf("%c ", representation[rank]);
X					cards++;
X				}
X			}
X			if (cards == 0) {
X				printf("- ");
X				cards++;
X			}
X		}
X		printf("\n");
X	}
X	printf("\n");
X}
X
Xcleanup_action() {
X    struct action *acp;
X    int player,i;
X		
X    for (acp=actionlist; acp!=0; acp = acp->ac_next) {
X	switch(acp->ac_type) {
X	default:
X	    assert(0);
X	    /*NOTREACHED*/
X	case ACT_PRINTALL:
X	    break;
X	case ACT_PRINT:
X	    for (player = COMPASS_NORTH; player<=COMPASS_WEST; player++) {
X		if (!(acp->ac_int1 & (1<<player)))
X		    continue;
X		printf("\n\n%s hands:\n\n\n\n", player_name[player]);
X		for (i=0; i<nprod; i+=4)
X		    printhands(i, deallist+i, player, nprod-i>4 ? 4 : nprod-i);
X		printf("\f");
X	    }
X	    break;
X	case ACT_AVERAGE:
X	    if (acp->ac_str1)
X		printf("%s: ", acp->ac_str1);
X	    printf("%g\n", (double) acp->ac_int1/nprod);
X	    break;
X	case ACT_FREQUENCY:
X	    printf("Frequency %s:\n", acp->ac_str1 ? acp->ac_str1 : "");
X	    if (acp->ac_u.acu_f.acuf_uflow)
X		printf("Low\t%8d\n", acp->ac_u.acu_f.acuf_uflow);
X	    for (i=acp->ac_u.acu_f.acuf_lowbnd; i <= acp->ac_u.acu_f.acuf_highbnd; i++)
X		printf("%5d\t%8d\n", i, acp->ac_u.acu_f.acuf_freqs[i-acp->ac_u.acu_f.acuf_lowbnd]);
X	    if (acp->ac_u.acu_f.acuf_oflow)
X		printf("High\t%8d\n", acp->ac_u.acu_f.acuf_oflow);
X	    break;
X	}
X    }
X}
X
Xint verbose = 1;
X
Xmain(argc, argv)
Xint argc;
Xchar **argv;
X{
X	extern int optind;
X	extern char *optarg;
X	char c;
X	int errflg = 0;
X
X	while ((c = getopt(argc, argv, "v")) != -1) {
X		switch(c) {
X		case 'v':
X			verbose ^= 1;
X			break;
X		case '?':
X			errflg = 1;
X			break;
X		}
X	}
X	if (argc - optind>1 || errflg) {
X		fprintf(stderr, "Usage: %s [-v] [inputfile]\n", argv[0]);
X		exit(-1);
X	}
X	if (optind < argc && freopen(argv[optind], "r", stdin) == NULL) {
X		perror(argv[optind]);
X		exit(-1);
X	}
X	newpack(fullpack);
X	emptypack(stacked_pack);
X	initdistr();
X	yyparse();
X	initrandom();
X	if (maxgenerate==0)
X		maxgenerate = 1000000;
X	if (maxproduce==0)
X		maxproduce =
X			actionlist == &defaultaction || will_print
X			? 40 : 100000;
X
X	setup_action();
X	setup_deal();
X	for(ngen=nprod=0; ngen<maxgenerate && nprod < maxproduce; ngen++) {
X		shuffle(curdeal);
X		analyze(curdeal, hs);
X		if (interesting()) {
X			action();
X			nprod++;
X		}
X	}
X	cleanup_action();
X	if (verbose)
X		printf("Generated %d hands, produced %d.\n", ngen, nprod);
X	return 0;
X}
+ END-OF-FILE dealer.c
chmod 'u=r,g=r,o=r' 'dealer.c'
echo 'SENT: -r--r--r--  1 sater       12668 Nov 15 22:26 dealer.c'
echo -n 'RCVD: '
/bin/ls -l dealer.c
echo 'Extracting defs.y'
sed 's/^X//' > defs.y << '+ END-OF-FILE defs.y'
X%{
X#include <stdio.h>
X#include <string.h>
X#include "tree.h"
X
Xstatic char rcsid[] = "$Header: /home/sater/bridge/dealer/RCS/defs.y,v 1.4 1992/11/15 22:01:03 sater Exp $";
X
Xint predeal_compass;		/* global variable for predeal communication */
X%}
X
X%union {
X	int	y_int;
X	char	*y_str;
X	struct tree *y_tree;
X	struct action *y_action;
X	char	y_distr[4];
X}
X
X%left OR2
X%left AND2
X%left CMPEQ, CMPNE
X%left CMPLT, CMPLE, CMPGT, CMPGE
X%left ARPLUS, ARMINUS
X%left ARTIMES, ARDIVIDE, ARMOD
X%nonassoc NOT
X
X%token GENERATE, PRODUCE, HCP, SHAPE, ANY, EXCEPT, CONDITION, ACTION
X%token PRINT, PRINTALL, AVERAGE, HASCARD, FREQUENCY, PREDEAL
X%token <y_int> NUMBER
X%token <y_str> HOLDING
X%token <y_str> STRING
X%token <y_str> IDENT
X%token <y_int> COMPASS
X%token <y_int> SUIT
X%token <y_int> CARD
X%token <y_distr> DISTR
X
X%type <y_tree>	expr
X%type <y_int> compass, printlist, shlprefix, any
X%type <y_distr> shape
X%type <y_action> actionlist action
X%type <y_str> optstring
X
X%start defs
X
X%%
X
Xdefs
X	: /* empty */
X	| defs def
X	;
X
Xdef
X	: GENERATE NUMBER
X		{ extern int maxgenerate; maxgenerate = $2; }
X	| PRODUCE NUMBER
X		{ extern int maxproduce; maxproduce = $2; }
X	| PREDEAL predealargs
X	| CONDITION expr
X		{ extern struct tree *decisiontree; decisiontree = $2; }
X	| expr
X		{ extern struct tree *decisiontree; decisiontree = $1; }
X	| IDENT '=' expr
X		{ new_var($1, $3); }
X	| ACTION actionlist
X		{ extern struct action *actionlist; actionlist = $2; }
X	;
X
Xpredealargs
X	: predealarg
X	| predealargs predealarg
X	;
X
Xpredealarg
X	:  COMPASS { predeal_compass = $1;} holdings
X	;
X
Xholdings
X	: HOLDING
X		{ predeal_holding(predeal_compass, $1); }
X	| holdings ',' HOLDING
X		{ predeal_holding(predeal_compass, $3); }
X	;
X
Xcompass
X	: COMPASS
X		{ extern int use_compass[NSUITS]; use_compass[$1] = 1; $$= $1; }
X	;
X
Xshape
X	: DISTR
X		{ bcopy($1, $$, 4); };
X	| NUMBER
X		{ if ($1<1000 || $1>=10000) yyerror("bad shape");
X		  $$[0] ='0'+$1/1000;  $$[1]='0'+$1/100%10;
X		  $$[2] ='0'+$1/10%10; $$[3]='0'+$1%10;
X		}
X	;
X
Xshlprefix
X	: ','
X		{ $$ = 0; }
X	| ARPLUS
X		{ $$ = 0; }
X	| /* empty */
X		{ $$ = 0; }
X	| ARMINUS
X		{ $$ = 1; }
X	;
X
Xany
X	: /* empty */
X		{ $$ = 0; }
X	| ANY
X		{ $$ = 1; }
X	;
X
Xshapelistel
X	: shlprefix any shape
X		{ insertshape($3, $2, $1); }
X	;
X		
Xshapelist
X	: shapelistel
X	| shapelist shapelistel
X	;
X
Xexpr
X	: NUMBER
X		{ $$ = newtree(TRT_NUMBER, NIL, NIL, $1, 0); }
X	| IDENT
X		{ $$ = var_lookup($1, 1); }
X	| SUIT '(' compass ')'
X		{ $$ = newtree(TRT_LENGTH, NIL, NIL, $1, $3); }
X	| HCP '(' compass ')'
X		{ $$ = newtree(TRT_HCPTOTAL, NIL, NIL, $3, 0); }
X	| HCP '(' compass ',' SUIT ')'
X		{ $$ = newtree(TRT_HCP, NIL, NIL, $3, $5); }
X	| SHAPE '(' compass ',' shapelist ')'
X		{ $$ = newtree(TRT_SHAPE, NIL, NIL, $3, 1<<(shapeno++)); }
X	| HASCARD '(' COMPASS ',' CARD ')'
X		{ $$ = newtree(TRT_HASCARD, NIL, NIL, $3, $5); }
X	| '(' expr ')'
X		{ $$ = $2; }
X	| expr CMPEQ expr
X		{ $$ = newtree(TRT_CMPEQ, $1, $3, 0, 0); }
X	| expr CMPNE expr
X		{ $$ = newtree(TRT_CMPNE, $1, $3, 0, 0); }
X	| expr CMPLT expr
X		{ $$ = newtree(TRT_CMPLT, $1, $3, 0, 0); }
X	| expr CMPLE expr
X		{ $$ = newtree(TRT_CMPLE, $1, $3, 0, 0); }
X	| expr CMPGT expr
X		{ $$ = newtree(TRT_CMPGT, $1, $3, 0, 0); }
X	| expr CMPGE expr
X		{ $$ = newtree(TRT_CMPGE, $1, $3, 0, 0); }
X	| expr AND2 expr
X		{ $$ = newtree(TRT_AND2, $1, $3, 0, 0); }
X	| expr OR2 expr
X		{ $$ = newtree(TRT_OR2, $1, $3, 0, 0); }
X	| expr ARPLUS expr
X		{ $$ = newtree(TRT_ARPLUS, $1, $3, 0, 0); }
X	| expr ARMINUS expr
X		{ $$ = newtree(TRT_ARMINUS, $1, $3, 0, 0); }
X	| expr ARTIMES expr
X		{ $$ = newtree(TRT_ARTIMES, $1, $3, 0, 0); }
X	| expr ARDIVIDE expr
X		{ $$ = newtree(TRT_ARDIVIDE, $1, $3, 0, 0); }
X	| expr ARMOD expr
X		{ $$ = newtree(TRT_ARMOD, $1, $3, 0, 0); }
X	| NOT expr
X		{ $$ = newtree(TRT_NOT, $2, NIL, 0, 0); }
X	;
Xactionlist
X	: action
X		{ $$ = $1; }
X	| action ',' actionlist
X		{ $$ = $1; $$->ac_next = $3; }
X	| /* empty */
X		{ $$ = 0; }
X	;
Xaction
X	: PRINTALL
X		{ praction();
X		  $$ = newaction(ACT_PRINTALL, NIL, (char *) 0, 0);
X		}
X	| PRINT '(' printlist ')'
X		{ praction();
X		  $$ = newaction(ACT_PRINT, NIL, (char *) 0, $3);
X		}
X	| AVERAGE optstring expr
X		{ $$ = newaction(ACT_AVERAGE, $3, $2, 0); }
X	| FREQUENCY optstring '(' expr ',' NUMBER ',' NUMBER ')'
X		{ $$ = newaction(ACT_FREQUENCY, $4, $2, 0);
X		  $$->ac_u.acu_f.acuf_lowbnd = $6;
X		  $$->ac_u.acu_f.acuf_highbnd = $8;
X		}
X	;
Xoptstring
X	: /* empty */
X		{ $$ = (char *) 0; }
X	| STRING
X		{ $$ = $1; }
X	;
Xprintlist
X	: COMPASS
X		{ $$ = (1<<$1); }
X	| printlist ',' COMPASS
X		{ $$ = $1|(1<<$3); }
X	;
X%%
X
Xstruct var {
X	struct var *v_next;
X	char *v_ident;
X	struct tree *v_tree;
X} *vars=0;
X
Xstruct tree *var_lookup(s, mustbethere)
Xchar *s;
X{
X	struct var *v;
X
X	for(v=vars; v!=0; v = v->v_next)
X		if (strcmp(s, v->v_ident)==0)
X			return v->v_tree;
X	if (mustbethere)
X		yyerror("unknown variable");
X	return 0;
X}
X
Xnew_var(s, t)
Xchar *s;
Xstruct tree *t;
X{
X	struct var *v;
X	char *mycalloc();
X
X	if (var_lookup(s, 0)!=0)
X		yyerror("redefined variable");
X	v = (struct var *) mycalloc(1, sizeof(*v));
X	v->v_next = vars;
X	v->v_ident = s;
X	v->v_tree = t;
X	vars = v;
X}
X
Xint lino=1;
X
Xyyerror(s)
Xchar *s;
X{
X
X	fprintf(stderr, "line %d: %s\n", lino, s);
X	exit(-1);
X}
X
Xint perm[24][4] = {
X	{	0,	1,	2,	3	},
X	{	0,	1,	3,	2	},
X	{	0,	2,	1,	3	},
X	{	0,	2,	3,	1	},
X	{	0,	3,	1,	2	},
X	{	0,	3,	2,	1	},
X	{	1,	0,	2,	3	},
X	{	1,	0,	3,	2	},
X	{	1,	2,	0,	3	},
X	{	1,	2,	3,	0	},
X	{	1,	3,	0,	2	},
X	{	1,	3,	2,	0	},
X	{	2,	0,	1,	3	},
X	{	2,	0,	3,	1	},
X	{	2,	1,	0,	3	},
X	{	2,	1,	3,	0	},
X	{	2,	3,	0,	1	},
X	{	2,	3,	1,	0	},
X	{	3,	0,	1,	2	},
X	{	3,	0,	2,	1	},
X	{	3,	1,	0,	2	},
X	{	3,	1,	2,	0	},
X	{	3,	2,	0,	1	},
X	{	3,	2,	1,	0	},
X};
X
Xint shapeno;
Xinsertshape(s, any, neg_shape)
Xchar s[4];
X{
X	int i,j,p;
X	int xcount=0, ccount=0;
X	char copy_s[4];
X
X	for (i=0;i<4;i++) {
X		if (s[i]=='x')
X			xcount++;
X		else
X			ccount += s[i]-'0';
X	}
X	switch(xcount) {
X	case 0:
X		if (ccount!=13)
X			yyerror("wrong number of cards in shape");
X		for (p=0; p<(any? 24 : 1); p++)
X			setshapebit(s[perm[p][3]]-'0', s[perm[p][2]]-'0',
X				s[perm[p][1]]-'0', s[perm[p][0]]-'0',
X				1<<shapeno, neg_shape);
X		break;
X	default:
X		if (ccount>13)
X			yyerror("too many cards in ambiguous shape");
X		bcopy(s, copy_s, 4);
X		for(i=0; copy_s[i] != 'x'; i++)
X			;
X		if (xcount==1) {
X			copy_s[i] = 13-ccount+'0';	/* could go above '9' */
X			insertshape(copy_s, any, neg_shape);
X		} else {
X			for (j=0; j<=13-ccount; j++) {
X				copy_s[i] = j+'0';
X				insertshape(copy_s, any, neg_shape);
X			}
X		}
X		break;
X	}
X}
X
Xstruct tree *newtree(type, p1, p2, i1, i2)
Xint type;
Xstruct tree *p1, *p2;
Xint i1,i2;
X{
X	char *mycalloc();
X	struct tree *p;
X
X	p = (struct tree *) mycalloc(1, sizeof(*p));
X	p->tr_type = type;
X	p->tr_leaf1 = p1;
X	p->tr_leaf2 = p2;
X	p->tr_int1 = i1;
X	p->tr_int2 = i2;
X	return p;
X}
X
Xstruct action *newaction(type, p1, s1, i1)
Xint type;
Xstruct tree *p1;
Xchar *s1;
Xint i1;
X{
X	char *mycalloc();
X	struct action *a;
X
X	a = (struct action *) mycalloc(1, sizeof(*a));
X	a->ac_type = type;
X	a->ac_expr1 = p1;
X	a->ac_str1 = s1;
X	a->ac_int1 = i1;
X	return a;
X}
X
Xchar *mystrcpy(s)
Xchar *s;
X{
X	char *cs;
X	char *mycalloc();
X
X	cs = mycalloc(strlen(s)+1, sizeof(char));
X	strcpy(cs, s);
X	return cs;
X}
X
Xpredeal_holding(compass, holding)
Xchar *holding;
X{
X	int suit;
X
X	suit = *holding++;
X	while (*holding) {
X		predeal(compass, make_card(*holding, suit));
X		holding++;
X	}
X}
X
X#include "scan.c"
+ END-OF-FILE defs.y
chmod 'u=r,g=r,o=r' 'defs.y'
echo 'SENT: -r--r--r--  1 sater        7238 Nov 15 23:01 defs.y'
echo -n 'RCVD: '
/bin/ls -l defs.y
echo 'Extracting scan.l'
sed 's/^X//' > scan.l << '+ END-OF-FILE scan.l'
X/* $Header: /home/sater/bridge/dealer/RCS/scan.l,v 1.2 1992/11/15 21:26:43 sater Exp $ */
X%%
X"&&"		return(AND2);
Xand		return(AND2);
X"||"		return(OR2);
Xor		return(OR2);
X"!"		return(NOT);
Xnot		return(NOT);
X"=="		return(CMPEQ);
X"!="		return(CMPNE);
X"<"		return(CMPLT);
X"<="		return(CMPLE);
X">"		return(CMPGT);
X">="		return(CMPGE);
X"+"		return(ARPLUS);
X"-"		return(ARMINUS);
X"*"		return(ARTIMES);
X"/"		return(ARDIVIDE);
X"%"		return(ARMOD);
Xgenerate	return(GENERATE);
Xproduce		return(PRODUCE);
Xpredeal		return(PREDEAL);
Xcondition	return(CONDITION);
Xaction		return(ACTION);
Xprint		return(PRINT);
Xprintall	return(PRINTALL);
Xaverage		return(AVERAGE);
Xfrequency	return(FREQUENCY);
Xnorth		{ yylval.y_int = COMPASS_NORTH; return COMPASS; }
Xeast		{ yylval.y_int = COMPASS_EAST; return COMPASS; }
Xsouth		{ yylval.y_int = COMPASS_SOUTH; return COMPASS; }
Xwest		{ yylval.y_int = COMPASS_WEST; return COMPASS; }
Xclubs		{ yylval.y_int = SUIT_CLUB; return SUIT; }
Xdiamonds	{ yylval.y_int = SUIT_DIAMOND; return SUIT; }
Xhearts		{ yylval.y_int = SUIT_HEART; return SUIT; }
Xspades		{ yylval.y_int = SUIT_SPADE; return SUIT; }
Xhcp		return(HCP);
Xhascard		return(HASCARD);
Xshape		return(SHAPE);
Xany		return(ANY);
X[CDHS]A?K?Q?J?T?9?8?7?6?5?4?3?2?	{ yylval.y_str = mystrcpy(yytext); return HOLDING; }
X[2-9TJQKA][CDHS]	{ yylval.y_int = make_card(yytext[0], yytext[1]);
X			  return(CARD);
X			}
X\"[^"]*\"	{ yytext[yyleng-1] = 0; yylval.y_str = mystrcpy(yytext+1); return STRING; }
X[0-9]+		{ yylval.y_int = atoi(yytext); return NUMBER; }
X[0-9x]{4}	{ strncpy(yylval.y_distr, yytext, 4); return DISTR; }
X[a-zA-Z][a-zA-Z0-9_]*	{ yylval.y_str = mystrcpy(yytext); return IDENT; }
X[ \t]		;
X\n		{ lino++; }
X^\#.*\n		{ lino++; }
X.		return(yytext[0]);
+ END-OF-FILE scan.l
chmod 'u=r,g=r,o=r' 'scan.l'
echo 'SENT: -r--r--r--  1 sater        1713 Nov 15 22:26 scan.l'
echo -n 'RCVD: '
/bin/ls -l scan.l
echo 'Extracting tree.h'
sed 's/^X//' > tree.h << '+ END-OF-FILE tree.h'
X/*
X * decision tree and other expression stuff
X */
X
Xstruct tree {
X	int		tr_type;
X	struct tree 	*tr_leaf1, *tr_leaf2;
X	int		tr_int1;
X	int		tr_int2;
X};
X
X#define NIL	((struct tree *) 0)
X
X#define SUIT_CLUB	0
X#define SUIT_DIAMOND	1
X#define SUIT_HEART	2
X#define SUIT_SPADE	3
X#define NSUITS		4
X
X#define MAKECARD(suit, rank)	(((suit)<<6)|(rank))
X#define C_SUIT(c)		((c)>>6)
X#define C_RANK(c)		((c)&0x3F)
X#define NO_CARD			0xFF
X
X#define COMPASS_NORTH	0
X#define COMPASS_EAST	1
X#define COMPASS_SOUTH	2
X#define COMPASS_WEST	3
X
X#define TRT_NUMBER	0
X#define TRT_AND2	1
X#define TRT_OR2		2
X#define TRT_CMPEQ	3
X#define TRT_CMPNE	4
X#define TRT_CMPLT	5
X#define TRT_CMPLE	6
X#define TRT_CMPGT	7
X#define TRT_CMPGE	8
X#define TRT_LENGTH	9
X#define TRT_ARPLUS	10
X#define TRT_ARMINUS	11
X#define TRT_ARTIMES	12
X#define TRT_ARDIVIDE	13
X#define TRT_ARMOD	14
X#define TRT_HCPTOTAL	15
X#define TRT_HCP		16
X#define TRT_SHAPE	17
X#define TRT_NOT		18
X#define TRT_HASCARD	19
X
X
X/*
X * Actions to be taken
X */
Xstruct action {
X	struct action	*ac_next;
X	int		ac_type;
X	struct tree	*ac_expr1;
X	int		ac_int1;
X	char		*ac_str1;
X	union {
X		struct {
X			long	acuf_lowbnd;
X			long	acuf_highbnd;
X			long	acuf_uflow;
X			long	acuf_oflow;
X			long	*acuf_freqs;
X		} acu_f;
X	} ac_u;
X};
X
X#define ACT_PRINTALL	0
X#define ACT_PRINT	1
X#define ACT_AVERAGE	2
X#define ACT_FREQUENCY	3
+ END-OF-FILE tree.h
chmod 'u=r,g=r,o=r' 'tree.h'
echo 'SENT: -r--r--r--  1 sater        1317 Nov 15 22:27 tree.h'
echo -n 'RCVD: '
/bin/ls -l tree.h
echo 'Extracting Descr.6c'
sed 's/^X//' > Descr.6c << '+ END-OF-FILE Descr.6c'
X# A typical play decision, here is the hand, the bidding and trick one
X#
X#	J x x x x x x
X#	J x
X#	x x
X#	9 5
X#
X#
X#	A
X#	A Q
X#	A
X#	A Q J 10 x x x x x
X#
X#	W	N	E	S
X#	p	p	1D	dbl
X#	1H	2S	3H	4C
X#	p	4S	p	6C
X#	all pass
X#
X# The lead is a small diamond to the K and A, how to play?
X# Two possibilities, play for the drop of the KC or lead the QC,
X# cross to the 9 and take the heart finesse.
X#
X# the inferences from bidding and trick one are encoded below
Xpredeal
X	north SJ865432, HJ5, D72, C95
X	west DQ5
X	south SA, HAQ, DA, CAQJT87642
X	east DKJ
Xcondition
X	hcp(east)>=10 && shape(west, x5xx) && shape(east, x45x + x46x + x47x)
Xaction
X	average "king drops" shape(west, xxx1),
X	average "finesse" hascard(east, KH)
+ END-OF-FILE Descr.6c
chmod 'u=r,g=r,o=r' 'Descr.6c'
echo 'SENT: -r--r--r--  1 sater         699 Nov 15 22:56 Descr.6c'
echo -n 'RCVD: '
/bin/ls -l Descr.6c
echo 'Extracting Descr.controls'
sed 's/^X//' > Descr.controls << '+ END-OF-FILE Descr.controls'
X# Calculates frequency of controls (A=2, K=1) in a strong balanced hand
Xcontrols=
X	2*hascard(north, AS) + hascard(north, KS) +
X	2*hascard(north, AH) + hascard(north, KH) +
X	2*hascard(north, AD) + hascard(north, KD) +
X	2*hascard(north, AC) + hascard(north, KC)
X
Xcondition
X	shape(north, any 4333 + any 4432 + any 5332) and
X		hcp(north)>=20 and hcp(north)<=22
X
Xaction
X	frequency "controls" (controls, 5, 9)
+ END-OF-FILE Descr.controls
chmod 'u=r,g=r,o=r' 'Descr.controls'
echo 'SENT: -r--r--r--  1 sater         404 Nov 15 22:56 Descr.controls'
echo -n 'RCVD: '
/bin/ls -l Descr.controls
echo 'Extracting Descr.notrump'
sed 's/^X//' > Descr.notrump << '+ END-OF-FILE Descr.notrump'
X# Generate hands for practicing bidding after 1NT
X
Xeast_notrump =
X	shape(east, any 4333 + any 4432 + any 5332 - 5xxx - x5xx)
X	&& hcp(east)>=15 && hcp(east)<=17
X
Xwest_interesting_low = hcp(west)>=7 and hcp(west)<=9 
Xwest_interesting_high = hcp(west)>=13 and hcp(west)<=18
X
Xcondition
X	east_notrump and ( west_interesting_low or west_interesting_high)
Xaction
X	print(east, west)
+ END-OF-FILE Descr.notrump
chmod 'u=r,g=r,o=r' 'Descr.notrump'
echo 'SENT: -r--r--r--  1 sater         375 Nov 15 22:56 Descr.notrump'
echo -n 'RCVD: '
/bin/ls -l Descr.notrump
echo 'Extracting Descr.weaktwo'
sed 's/^X//' > Descr.weaktwo << '+ END-OF-FILE Descr.weaktwo'
X# calculates some statistics of hands deemed to be weak two's
X# I do not claim this definition of a weak two will satisfy everybody
X
Xweaktwospades = 
X	shape(north, 6xxx - any 0xxx) and
X	hcp(north) >= 5 and hcp(north) <= 11 and
X	hcp(north, spades) >= hcp(north)/2
X
Xheartfit =
X	hearts(north) + hearts(south) >= 8
X
Xcondition weaktwospades
Xaction
X	frequency "points" (hcp(north), 5, 11) ,
X	average "hearts" hearts(north) ,
X	average "Ace of spades" hascard(north, AS) ,
X	average "Jack of spades" hascard(north, JS) ,
X	average "Heart fit" heartfit
+ END-OF-FILE Descr.weaktwo
chmod 'u=r,g=r,o=r' 'Descr.weaktwo'
echo 'SENT: -r--r--r--  1 sater         542 Nov 15 22:56 Descr.weaktwo'
echo -n 'RCVD: '
/bin/ls -l Descr.weaktwo
exit 0
Newsgroups: rec.games.bridge