[comp.sources.x] v04i030: xmahjongg

argv@island.uu.net (Dan Heller) (06/21/89)

Submitted-by: Jeff S. Young <jsy@cray.com>
Posting-number: Volume 4, Issue 30
Archive-name: xmahjongg/part01



[ This one doesn't work in color. --argv ]

#! /bin/sh
: This is a shar archive.  Extract with sh, not csh.
mkdir Xmahjongg
cd Xmahjongg
mkdir tiles
echo x - ./Imakefile
cat > ./Imakefile << '7623!Funky!Stuff!'
# 
#	xmahjongg - Mahjongg game
# 
#	Copyright (c) 1989 by Jeff S. Young.  All rights reserved under the 
#	copyright laws of the United States.
# 
LOCAL_LIBRARIES = $(XLIB) 
SRCS	= xmahjongg.c draw.c event.c initial.c packet.c play.c random.c tiles.c
OBJS	= xmahjongg.o draw.o event.o initial.o packet.o play.o random.o tiles.o

ComplexProgramTarget(xmahjongg)
7623!Funky!Stuff!
echo x - ./xmahjongg.h
cat > ./xmahjongg.h << '7623!Funky!Stuff!'
/*
 ******************************************************************************
 *									      *
 *	Copyright (c) 1989 by Jeff S. Young.  All rights reserved under the   *
 *	copyright laws of the United States.			      	      *
 *									      *
 ******************************************************************************
 */

/*
 *	Include file for xmahjongg
 */
typedef unsigned char	Uchar;

#define	TILES		144

#define ROWS		9
#define COLS		13
#define LEVS		6

#define	TRUE		1
#define	FALSE		0

#define FREE		0
#define	USED		1

/*
 *	Dimensions for boxes, tiles and options
 */
#define	TILE_WIDTH	64
#define	TILE_HEIGHT	64

#define	ICON_WIDTH	64
#define	ICON_HEIGHT	64

#define	SHADE_WIDTH	4
#define	SHADE_HEIGHT	64

#define	BOARD_WIDTH	(COLS-1)*TILE_WIDTH
#define	BOARD_HEIGHT	(ROWS-1)*TILE_WIDTH

#define	OPTION_WIDTH	48
#define	OPTION_HEIGHT	32

#define	CURSOR_WIDTH	16
#define	CURSOR_HEIGHT	16

#define LETTER_WIDTH	64
#define LETTER_HEIGHT	99

#define WINDOW_WIDTH	1000
#define WINDOW_HEIGHT	800

#define BORDER_WIDTH	2

/*
 *	Upper left coordinates for items
 */
#define X_TILE		20
#define Y_TILE		181

#define X_DONE		800
#define Y_DONE		100

#define X_SAME		800
#define Y_SAME		30

#define X_QUIT		900
#define Y_QUIT		100

#define X_NEW		900
#define Y_NEW		30

#define	X_COPY		800
#define	Y_COPY		150

#define	X_RIGHTS	800
#define	Y_RIGHTS	162

#define	X_BOARD		345
#define	Y_BOARD		40

#define	X_NAMES		260
#define	Y_NAMES		55

#define	X_SCORE		345
#define	Y_SCORE		55

#define in_box(x0, y0, xlen, ylen) 					\
	((x0 <= x_coor) && (x_coor <= x0+xlen) &&			\
	 (y0 <= y_coor) && (y_coor <= y0+ylen))

/*
 *	Tile structure
 */
typedef	struct tile	Tile;

struct tile {
	int	x;
	int	y;
	int	row;
	int	col;
	int	lev;
	int	state;
	Uchar	*data;
	Uchar	*type;
};

/*
 *	Tournament mode variables and structures
 */
#define	XPORT		3857		/* port value for tournaments */
#define	MAX_PLAYERS	5
#define MAX_BOARDS	7

typedef	struct player	Player;

struct player {
	char	name[12];
	char	machine[20];
	long	x;
	long	y;
	long	fd;
	long	type;
	long	port;
	long	done;
	long	quit;
	long	total;
	long	tiles[MAX_BOARDS];
	long	board[MAX_BOARDS];
};

typedef struct packet	Packet;

struct packet {
	short	type;
	short	port;
	short	extra;
	short	tiles;
	long	board;
	char	name[12];
};

#define	GAME_START	1
#define	GAME_PLAY	2
#define	GAME_DONE	3
#define	GAME_QUIT	4

/*
 *	X Window stuff
 */
#define FONT			"6x10"
#define XMahjonggEvents		ButtonPressMask | ExposureMask
7623!Funky!Stuff!
echo x - ./xmahjongg.c
cat > ./xmahjongg.c << '7623!Funky!Stuff!'
/*
 ******************************************************************************
 *									      *
 *	Copyright (c) 1989 by Jeff S. Young.  All rights reserved under the   *
 *	copyright laws of the United States.			      	      *
 *									      *
 ******************************************************************************
 */

#include <pwd.h>
#include <stdio.h>
#include <string.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include "xmahjongg.h"

extern	int optind;
extern	int opterr;
extern	char *optarg;

int	maxfds = 0;
int	playfds = 0;
int	num_games = 0;
int	done_count = 0;
int	tourn_flag = 0;
int	num_players = 0;
int	keep_playing = 0;
int	reverse_video = 0;
int	tiles_remaining;
long	seed = 0;
long	mask[33];
long	buffer[2048];
Uchar	*tile_data[TILES];
char	*display_name = NULL;
Tile	*tile1p = NULL;
Tile	*tile2p = NULL;
Tile	tiles[ROWS][COLS][LEVS];
Player	player[MAX_PLAYERS];
Player	*mypp = NULL;
Player	*pp = NULL;

int	screen;
int	xcopyright;
int	ycopyright;
int	fore_color;
int	back_color;
int	BorderColor;

Font		font;
int		XGameFD;
GC		XGameGC0;
GC		XGameGC1;
Cursor		cursor;
Pixmap		XGameIcon;
Window		XGameWindow;
Display		*XGameDisplay;
XSizeHints	XGameHints;
XFontStruct 	*XGameFont;
XEvent		XGameEvent;


main(argc, argv)
int argc;
char *argv[];
{

	get_parameters(argc, argv);

	while(1) {
		initialize();
		keep_playing = 1;
		packet_send(GAME_START);

		while(keep_playing != 0) {
			event_wait();
		};
	};
}

get_parameters(argc, argv)
int argc;
char *argv[];
{
	int i, j, c;
	struct passwd *pwp;
	char *cp, hostname[20];
	Player tmp, *pp = player;

/*
 *	Parse the arguments.
 */
	while ((c = getopt(argc, argv, "rb:d:n:p:")) != EOF) {
		switch (c) {
			case 'b':
				seed = -atoi(optarg);
				break;
			case 'd':
				display_name = optarg;
				break;
			case 'n':
				num_games = atoi(optarg);
				break;
			case 'r':
				reverse_video = 1;
				break;
			case 'p':
				tourn_flag = 1;
				if ((cp = strchr(optarg, '@')) == NULL) usage();
				strncpy(pp->name, optarg, cp-optarg);
				strcpy(pp->machine, cp+1);
				num_players++;
				pp++;
				break;
			default:
				usage();
				break;
		};
	};

/*
 *	Set up the tournament if requested.
 */
	if (num_players == 0) return(0);
	if (num_games == 0) num_games = 3;

	if ((pwp = getpwuid(getuid())) == NULL) {
		fprintf(stderr, "can't getpwuid\n");
		exit(1);
	};

	gethostname(hostname, 20);
	mypp = &player[num_players++];
	strcpy(mypp->name, pwp->pw_name);
	strcpy(mypp->machine, hostname);

	for (i = 0; i < num_players-1; i++) {
		for (j = i+1; j < num_players; j++) {
			if (strcmp(player[i].name, player[j].name) > 0) {
				tmp = player[i];
				player[i] = player[j];
				player[j] = tmp;
			};
		};
		
	};

	for (i = 0; i < num_players; i++) {
		player[i].x = X_SCORE;
		player[i].y = Y_SCORE+15*i;
		player[i].port = XPORT+i;
		player[i].done = -1;
		player[i].total = 144*num_games;

		for (j = 0; j < MAX_BOARDS; j++) {
			player[i].tiles[j] = 2*TILES;
		};

		if (strcmp(player[i].name, pwp->pw_name) < 0) {
			player[i].type = 'C';
		} else if (strcmp(player[i].name, pwp->pw_name) > 0) {
			player[i].type = 'A';
		} else {
			mypp = &player[i];
			player[i].type = 'M';
		};
	};

	return(0);
}

usage() {

	fprintf(stderr, "usage: xmahjongg [-b #] [-n #] [-d display] [-r] ");
	fprintf(stderr, "[-p n1@m1] [...] [-p nx@mx]\n");
	exit(1);
}

XError(string)
char *string;
{

	fprintf(stderr, "%s\n", string);
	exit(1);
}
7623!Funky!Stuff!
echo x - ./initial.c
cat > ./initial.c << '7623!Funky!Stuff!'
/*
 ******************************************************************************
 *									      *
 *	Copyright (c) 1989 by Jeff S. Young.  All rights reserved under the   *
 *	copyright laws of the United States.			      	      *
 *									      *
 ******************************************************************************
 */

#include <netdb.h>
#include <stdio.h>
#include <signal.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/cursorfont.h>
#include "xmahjongg.h"
#include "tilenames.h"

extern	int errno;

extern	int maxfds;
extern	int playfds;
extern	int done_count;
extern	int tourn_flag;
extern	int num_players;
extern	int reverse_video;
extern	int tiles_remaining;
extern	long seed;
extern	long mask[];
extern	long buffer[];
extern	Uchar *tile_data[];
extern	char *display_name;
extern	Tile *tile1p, *tile2p;
extern	Tile tiles[ROWS][COLS][LEVS];
extern	Player player[MAX_PLAYERS];
extern	Player *pp, *mypp;
extern	Packet packet;

extern	int screen;
extern	int xcopyright;
extern	int ycopyright;
extern	int fore_color;
extern	int back_color;
extern	int BorderColor;

extern	Font font;
extern	int XGameFD;
extern	GC XGameGC0;
extern	GC XGameGC1;
extern	Cursor cursor;
extern	Pixmap XGameIcon;
extern	Window XGameWindow;
extern	Display *XGameDisplay;
extern	XSizeHints XGameHints;
extern	XEvent XGameEvent;
extern	XFontStruct *XGameFont;

XWMHints XGameWMHints;

int	initial_flag = 0;
char	copy[]   = " (c) 1989 by Jeff Young. ";
char	rights[] = " All rights reserved.    ";
char copyright[] = " Copyright (c) 1989 by Jeff Young (exceptions).  All rights reserved. ";

initialize() {

	initialize_data();
	initialize_pool();
	initialize_tiles();
	initialize_socket();
	initialize_window();
	initialize_display();

	return(0);
}

initialize_data() {
	int i, j, k;

	done_count = 0;
	tiles_remaining = TILES;
	if (initial_flag != 0) return(0);

/*
 *	Initialize the bitmap pointers for shuffling into the pool.
 */
	tile_data[ 0] = number_0_bits;
	tile_data[ 1] = number_1_bits;
	tile_data[ 2] = number_2_bits;
	tile_data[ 3] = number_3_bits;
	tile_data[ 4] = number_4_bits;
	tile_data[ 5] = number_5_bits;
	tile_data[ 6] = number_6_bits;
	tile_data[ 7] = number_7_bits;
	tile_data[ 8] = number_8_bits;
	tile_data[ 9] = number_9_bits;
	tile_data[10] = dragon_green_bits;
	tile_data[11] = dragon_white_bits;
	tile_data[12] = dragon_red_bits;
	tile_data[13] = season_spring_bits;
	tile_data[14] = season_summer_bits;
	tile_data[15] = season_autumn_bits;
	tile_data[16] = season_winter_bits;
	tile_data[17] = plant_bamboo_bits;
	tile_data[18] = plant_orchid_bits;
	tile_data[19] = plant_plum_bits;
	tile_data[20] = plant_mum_bits;
	tile_data[21] = direction_north_bits;
	tile_data[22] = direction_south_bits;
	tile_data[23] = direction_east_bits;
	tile_data[24] = direction_west_bits;
	tile_data[25] = circle_1_bits;
	tile_data[26] = circle_2_bits;
	tile_data[27] = circle_3_bits;
	tile_data[28] = circle_4_bits;
	tile_data[29] = circle_5_bits;
	tile_data[30] = circle_6_bits;
	tile_data[31] = circle_7_bits;
	tile_data[32] = circle_8_bits;
	tile_data[33] = circle_9_bits;
	tile_data[34] = bamboo_1_bits;
	tile_data[35] = bamboo_2_bits;
	tile_data[36] = bamboo_3_bits;
	tile_data[37] = bamboo_4_bits;
	tile_data[38] = bamboo_5_bits;
	tile_data[39] = bamboo_6_bits;
	tile_data[40] = bamboo_7_bits;
	tile_data[41] = bamboo_8_bits;
	tile_data[42] = bamboo_9_bits;
	tile_data[43] = ideograph_1_bits;
	tile_data[44] = ideograph_2_bits;
	tile_data[45] = ideograph_3_bits;
	tile_data[46] = ideograph_4_bits;
	tile_data[47] = ideograph_5_bits;
	tile_data[48] = ideograph_6_bits;
	tile_data[49] = ideograph_7_bits;
	tile_data[50] = ideograph_8_bits;
	tile_data[51] = ideograph_9_bits;

	return(0);
}

initialize_pool() {
	int i, j, k;
	int seed1, seed2;
	long save;
	struct timeval tv;
	struct timezone tz;

	if (seed == 0) {
		gettimeofday(&tv, &tz);
		seed = 1 + (tv.tv_sec%100000);
	} else if (seed < 0) {
		seed = (-seed)%100000;
	} else {
		seed = 1 + (random_next(seed)%100000);
	};
	random_init(seed);

/*
 *	Place the circle, bamboo, and ideograph tiles in the pool
 */
	for (j = 0, k = 0; j < 4; j++) {
		for (i = 21; i <= 51; i++) {
			buffer[k++] = i;
		};
	};

/*
 *	Place the dragon tiles in the pool
 */
	for (j = 0; j < 4; j++) {
		for (i = 10; i <= 12; i++) {
			buffer[k++] = i;
		};
	};

/*
 *	Place the season and plant tiles in the pool
 */
	for (i = 13; i <= 20; i++) {
		buffer[k++] = i;
	};

/* 
 *	Shuffle the pool of tiles
 */
	for (i = 0; i < 16384; i++) {
		seed1 = (random_next() & 0xfffffff)%TILES;
		seed2 = (random_next() & 0xfffffff)%TILES;
		save = buffer[seed1];
		buffer[seed1] = buffer[seed2];
		buffer[seed2] = save;
	};

	return(0);
}

initialize_tiles() {
	int i, j, k;

/*
 *	Mark the tiles as free
 */
	for (i = 0; i < ROWS; i++) {
		for (j = 0; j < COLS; j++) {
			for (k = 0; k < LEVS; k++) {
				tiles[i][j][k].state = FREE;
			};
		};
	};

/*
 *	Mark the tiles on level 0 that are in use
 */
	tiles[0][1][0].state = USED;
	tiles[0][2][0].state = USED;
	tiles[0][3][0].state = USED;

/*
 *	Mark the tiles on level 1 that are in use
 */
	for (i = 3; i<= 6; i++) {
		for (j = 2; j <= 11; j++) {
			tiles[i][j][1].state = USED;
		};
	};

	for (j = 1; j <= 12; j++) {
		tiles[1][j][1].state = USED;
		tiles[8][j][1].state = USED;
	};

	for (j = 3; j <= 10; j++) {
		tiles[2][j][1].state = USED;
		tiles[7][j][1].state = USED;
	};

	tiles[4][01][1].state = USED;
	tiles[4][12][1].state = USED;
	tiles[5][01][1].state = USED;
	tiles[5][12][1].state = USED;

/*
 *	Mark the tiles on level 2 that are in use
 */
	for (i = 2; i <= 7; i++) {
		for (j = 4; j <= 9; j++) {
			tiles[i][j][2].state = USED;
		};
	};

/*
 *	Mark the tiles on level 3 that are in use
 */
	for (i = 3; i <= 6; i++) {
		for (j = 5; j <= 8; j++) {
			tiles[i][j][3].state = USED;
		};
	};

/*
 *	Mark the tiles on level 4 that are in use
 */
	for (i = 4; i <= 5; i++) {
		for (j = 6; j <= 7; j++) {
			tiles[i][j][4].state = USED;
		};
	};

/*
 *	Mark the tiles on level 5 that are in use
 */
	tiles[0][0][5].state = USED;

/*
 *	Setup the tiles by assigning them their various pictures.
 */
	for (i = 0; i < ROWS; i++) {
		for (j = 0; j < COLS; j++) {
			for (k = 0; k < LEVS; k++) {
				if (tiles[i][j][k].state == USED) {
					setup_tile(i, j, k);
				};
			};
		};
	};

/*
 *	Set the tile pointers to NULL for starters.
 */
	tile1p = NULL;
	tile2p = NULL;

	return(0);
}

initialize_socket() {
	int i, s;
	int namelen;
	char on = 1;
	struct sockaddr_in name;
	struct hostent *hp, *gethostbyname();

	if (tourn_flag != 1) return(0);
	printf("attempting connections\n");

/*
 *	Attempt connects
 */
	for (i = 0, pp = player; i < num_players; i++, pp++) {
		if (pp->type != 'C') continue;

		namelen = sizeof(name);
		name.sin_family = AF_INET;
		name.sin_port = htons(pp->port);
		hp = gethostbyname(pp->machine);
		bcopy(hp->h_addr, (char *)&name.sin_addr, hp->h_length);

		while (1) {
			if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
				sleep(1);
			} else if (connect(s, (char *)&name, namelen) < 0) {
				close(s);
				sleep(1);
			} else {
				pp->fd = s;
				break;
			};
		};
	};

/*
 *	Attempt accepts
 */
	name.sin_family = AF_INET;
	name.sin_port = htons(mypp->port);
	name.sin_addr.s_addr = 0;
	namelen = sizeof(name);

	if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
		fprintf(stderr, "can't open socket (%d)\n", errno);
		exit(1);
	} else if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, 1) < 0) {
		fprintf(stderr, "can't reset socket (%d)\n", errno);
		exit(1);
	} else if (bind(s, (char *)&name, namelen) < 0) {
		fprintf(stderr, "can't bind socket (%d)\n", errno);
		exit(1);
	} else if (listen(s, 5) < 0) {
		fprintf(stderr, "can't listen socket (%d)\n", errno);
		exit(1);
	};

	for (i = 0, pp = player; i < num_players; i++, pp++) {
		if (pp->type != 'A') continue;
		namelen = sizeof(name);

		if ((pp->fd = accept(s, (char *)&name, &namelen)) < 0) {
			sleep(1);
		};
	};

	for (i = 0, pp = player; i < num_players; i++, pp++) {
		if (pp->type != 'M') playfds |= (1 << pp->fd);
	};

	tourn_flag = 2;
	printf("connections established\n");

	return(0);
}

initialize_window() {
	int init_x, init_y;

/*
 *	Only initialize the window if the initial_flag is zero
 */
	if (initial_flag != 0) {
		XClearWindow(XGameDisplay, XGameWindow);
		return(0);
	};

/*
 *	Open up the display.
 */
	if ((XGameDisplay = XOpenDisplay(display_name)) == NULL) {
		XError("no display");
	};

	screen = DefaultScreen(XGameDisplay);
	XGameFD = ConnectionNumber(XGameDisplay);

	if (reverse_video == 0) {
		fore_color = WhitePixel(XGameDisplay, screen);
		back_color = BlackPixel(XGameDisplay, screen);
	} else {
		fore_color = BlackPixel(XGameDisplay, screen);
		back_color = WhitePixel(XGameDisplay, screen);
	};

/*
 *	Get the font for text printing
 */
	font = XLoadFont(XGameDisplay, FONT);
	XGameFont = XQueryFont(XGameDisplay, font);

/*
 *	Define an icon for the game
 */
	XGameIcon = XCreateBitmapFromData(
		XGameDisplay, DefaultRootWindow(XGameDisplay),
		icon_tiles_bits, ICON_WIDTH, ICON_HEIGHT);

/*
 *	Open the main window.
 */
	init_x = (DisplayWidth(XGameDisplay, screen)-WINDOW_WIDTH)/2;
	init_y = (DisplayHeight(XGameDisplay, screen)-WINDOW_HEIGHT)/2;

	XGameWindow = XCreateSimpleWindow(
		XGameDisplay, DefaultRootWindow(XGameDisplay),
		init_x, init_y, WINDOW_WIDTH, WINDOW_HEIGHT,
		BORDER_WIDTH, fore_color, back_color);

	if (XGameWindow == 0) XError("no window");

	XSetStandardProperties(
		XGameDisplay, XGameWindow,
		"XMahjongg", NULL,
		XGameIcon, NULL, 0,
		&XGameHints);

	XGameGC0 = XCreateGC(XGameDisplay, XGameWindow, 0, 0);
	XGameGC1 = XCreateGC(XGameDisplay, XGameWindow, 0, 0);

	XSetForeground(XGameDisplay, XGameGC0, fore_color);
	XSetBackground(XGameDisplay, XGameGC0, back_color);
	XSetForeground(XGameDisplay, XGameGC1, back_color);
	XSetBackground(XGameDisplay, XGameGC1, fore_color);

	XSetFont(XGameDisplay, XGameGC0, font);
	XSetFont(XGameDisplay, XGameGC1, font);

/*
 *	Select window exposure events to see if the contents of the window
 *	have been erased or altered.
 */
	XSelectInput(XGameDisplay, XGameWindow, XMahjonggEvents);
	XMapRaised(XGameDisplay, XGameWindow);
	XNextEvent(XGameDisplay, &XGameEvent);

/*
 *	Define the cursor for the game
 */
	cursor = XCreateFontCursor(XGameDisplay, XC_left_ptr);
	XDefineCursor(XGameDisplay, XGameWindow, cursor);

/*
 *	Map game window to screen.
 */
	XMapRaised(XGameDisplay, XGameWindow);

/*
 *	Display the game name and copyright notice
 */
	draw_line(0, 200, 1000, 200);

	draw_letter(letter_x_bits,  42, 50);
	draw_letter(letter_m_bits, 148, 50);
	draw_letter(letter_a_bits, 254, 50);
	draw_letter(letter_h_bits, 360, 50);
	draw_letter(letter_j_bits, 466, 50);
	draw_letter(letter_o_bits, 572, 50);
	draw_letter(letter_n_bits, 678, 50);
	draw_letter(letter_g_bits, 784, 50);
	draw_letter(letter_g_bits, 890, 50);

	xcopyright = (1000 - strlen(copyright)*XGameFont->max_bounds.width)/2;
	ycopyright = (200 - XGameFont->max_bounds.ascent - 10);
	draw_string(copyright, xcopyright, ycopyright, XGameGC1);

/*
 *	Display the tiles on the screen before the start of the game.  Order:
 *	dragons, seasons, plants, directions, circles, bamboos, ideographs
 *
 */
	draw_data();

	XFlush(XGameDisplay);
	sleep(5);

/*
 *	Blank out the screen prior to painting it with the game board.
 */
	XClearWindow(XGameDisplay, XGameWindow);
	XFlush(XGameDisplay);

/*
 *	Find the maximum file descriptor for the select system call and set
 *	the initialization flag.
 */
	initial_flag++;
	if ((maxfds = getdtablesize()) > 32) maxfds = 32;

	return(0);
}

initialize_display() {
	int x, y;
	int i, j, k;
	char ascii[80];
	char string[80];

/*
 *	Set up the mask array for future tile drawing
 */
	for (i = 1, mask[0] = 0; i < 33; i++) {
		mask[i] = 2*mask[i-1] + 1;
	};

/*
 *	Display the initial tile setup
 */
	draw_tile(0, 3);
	draw_tile(0, 2);

	for (j = COLS-1; j >= 0; j--) {
		for (i = 1; i < ROWS; i++) {
			draw_tile(i, j);
		};
	};

	draw_tile(0, 1);
	draw_tile(0, 0);

/*
 *	Display the count of remaining tiles and the board number
 */
	sprintf(string, "Board number : %d", seed);
	draw_string(string, 55, 55, XGameGC0);
	draw_count(number_0_bits);

/*
 *	Draw the option boxes
 */
	draw_line(0, 200, 1000, 200);

	draw_option(done_bits, X_DONE, Y_DONE, done_count);
	draw_option(same_bits, X_SAME, Y_SAME, 0);
	draw_option(quit_bits, X_QUIT, Y_QUIT, 0);
	draw_option(new_bits,  X_NEW,  Y_NEW, 0);

/*
 *	Draw the small copyright notices
 */
	ycopyright = XGameFont->ascent + XGameFont->descent + 1;
	draw_string(copy, X_COPY, Y_COPY, XGameGC1);
	draw_string(rights, X_COPY, Y_COPY+ycopyright, XGameGC1);
	XFlush(XGameDisplay);

/*
 *	If in tournament mode, then reprint the current scores.
 */
	if (tourn_flag != 0) {
		for (i = 0, pp = player; i < num_players; i++, pp++) {
			draw_user(pp, GAME_START);
		};
	};

	return(0);
}

setup_tile(row, col, lev)
int row, col, lev;
{
	Uchar *spring_tile = tile_data[13];
	Uchar *winter_tile = tile_data[16];
	Uchar *bamboo_tile = tile_data[17];
	Uchar *mum_tile = tile_data[20];
	Tile *tp = &tiles[row][col][lev];

/*
 *	Set up the values for positioning and tile picture
 */
	tp->row = row;
	tp->col = col;
	tp->lev = lev;
	tp->x = X_TILE + (col*TILE_WIDTH) + (lev*SHADE_WIDTH);
	tp->y = Y_TILE + (row*TILE_WIDTH) - (lev*SHADE_WIDTH);
	tp->data = tile_data[get_tile()];
	if ((spring_tile <= tp->data) && (tp->data <= winter_tile)) {
		tp->type = spring_tile;
	} else if ((bamboo_tile <= tp->data) && (tp->data <= mum_tile)) {
		tp->type = bamboo_tile;
	} else {
		tp->type = tp->data;
	};

/*
 *	Special positioning for the end tiles and the top tile
 */
	if (lev == 0) {
		if (col == 1) {
			tp->x = X_TILE + 4;
			tp->y = Y_TILE + (04*TILE_WIDTH) + 32;
		} else if (col == 2) {
			tp->x = X_TILE + (13*TILE_WIDTH) + 4;
			tp->y = Y_TILE + (04*TILE_WIDTH) + 32;
		} else if (col == 3) {
			tp->x = X_TILE + (14*TILE_WIDTH) + 4;
			tp->y = Y_TILE + (04*TILE_WIDTH) + 32;
		};
	} else if (lev == 5) {
		tp->x = X_TILE + (06*TILE_WIDTH) + 52;
		tp->y = Y_TILE + (04*TILE_WIDTH) + 12;
	};

	return(0);
}

get_tile() {
	int i, j;

	i = random_next()%TILES;
	j = ((random_next()%2) == 0) ? 1 : -1;

/*
 *	Walk through the tile pool looking for an unused tile
 */
	while (buffer[i] == 0) {
		i = (i+j)%TILES;
		if (i < 0) i = TILES-1;
	};

	j = buffer[i];
	buffer[i] = 0;

	return(j);
}

7623!Funky!Stuff!
echo x - ./event.c
cat > ./event.c << '7623!Funky!Stuff!'
/*
 ******************************************************************************
 *									      *
 *	Copyright (c) 1989 by Jeff S. Young.  All rights reserved under the   *
 *	copyright laws of the United States.			      	      *
 *									      *
 ******************************************************************************
 */

#include <stdio.h>
#include <signal.h>
#include <sys/time.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include "xmahjongg.h"
#include "tilenames.h"

extern	int seed;
extern	int maxfds;
extern	int playfds;
extern	int num_games;
extern	int done_count;
extern	int tourn_flag;
extern	int num_players;
extern	int keep_playing;
extern	int tiles_remaining;
extern	long buffer[];
extern	long game_board[];
extern	Uchar *tile_data[];
extern	Tile *tile1p, *tile2p;
extern	Tile tiles[ROWS][COLS][LEVS];
extern	Player player[MAX_PLAYERS];
extern	Player *pp, *mypp;

extern	int screen;
extern	int fore_color;
extern	int back_color;
extern	int BorderColor;

extern	Font font;
extern	int XGameFD;
extern	GC XGameGC0;
extern	GC XGameGC1;
extern	Window XGameWindow;
extern	Display *XGameDisplay;
extern	XSizeHints XGameHints;
extern	XEvent XGameEvent;
extern	XFontStruct *XGameFont;

int	x_coor;
int	y_coor;

event_wait() {
	int readfds;
	int events = 0;
	int exposes = 0;
	XEvent event;

	int i;
/*
 *	If there are no events pending, then sleep until one occurs.
 */
wait_event:
	if ((events = XEventsQueued(XGameDisplay, QueuedAfterReading)) == 0) {
		readfds = (1 << XGameFD) | playfds;
		i=select(maxfds, &readfds, NULL, NULL, NULL);
	};

/*
 *	An event has occurred.  Let's process it.
 */
	if ((readfds & playfds) != 0) {
		event_packet(readfds);
	};

next_event:
	if (events == 0) goto wait_event;
	XNextEvent(XGameDisplay, &XGameEvent);
	switch (XGameEvent.type) {
		case Expose:
			if (XGameEvent.xexpose.count != 0) goto next_event;
			event_paint();
			break;
		case ButtonPress:
			event_button();
			break;
		default:
			break;
	};

	return(0);
}

event_packet(readfds)
int readfds;
{
	int i;

	for (i = 0, pp = player; i < num_players; i++, pp++) {
		if (pp->type == 'M') continue;
		if ((readfds & (1 << pp->fd)) != 0) {
			packet_recv(pp->fd);
		};
	};

	return(0);
}

event_paint() {

	XClearWindow(XGameDisplay, XGameWindow);
	initialize_display();

	return(0);
}

event_button() {
	int i, j, k;

	i = XGameFont->ascent;
	j = XGameFont->descent;
	k = (200-(Y_COPY+i+j+1))/2 + (i+j+1)/2;
	XClearArea(
		XGameDisplay, XGameWindow,
		800, 200-k-1, 199, k, False);

	XFlush(XGameDisplay);

	x_coor = XGameEvent.xbutton.x;
	y_coor = XGameEvent.xbutton.y;

	if (in_box(X_NEW, Y_NEW, OPTION_WIDTH, OPTION_HEIGHT)) {
		event_new();
	} else if (in_box(X_DONE, Y_DONE, OPTION_WIDTH, OPTION_HEIGHT)) {
		event_done();
	} else if (in_box(X_SAME, Y_SAME, OPTION_WIDTH, OPTION_HEIGHT)) {
		event_same();
	} else if (in_box(X_QUIT, Y_QUIT, OPTION_WIDTH, OPTION_HEIGHT)) {
		event_quit();
	} else {
		event_tile();
	};

	return(0);
}

event_new() {

	if (tourn_flag != 0) {
		if (mypp->done < num_games-1) {
			keep_playing = 0;
			XClearWindow(XGameDisplay, XGameWindow);
		} else {
			XBell(XGameDisplay, 100);
			XFlush(XGameDisplay);
		};
	} else {
		keep_playing = 0;
		XClearWindow(XGameDisplay, XGameWindow);
	};

	return(0);
}

event_same() {

	if (num_games == 0) {
		seed = -seed;
		keep_playing = 0;
		XClearWindow(XGameDisplay, XGameWindow);
	} else {
		XBell(XGameDisplay, 100);
		XFlush(XGameDisplay);
	};

	return(0);
}

event_done() {
	int i, j, k;
	int matches = 0;
	char string[80];
	Tile *t1p, *t2p;

	draw_option(done_bits, X_DONE, Y_DONE, done_count);

	for(i = 0, t1p = &tiles[0][0][0]; i < ROWS*COLS*LEVS-1; i++, t1p++) {
		if (t1p->state != USED) continue;
		if (not_free(t1p->row, t1p->col, t1p->lev)) continue;
		for(j = i+1, t2p = t1p+1; j < ROWS*COLS*LEVS; j++, t2p++) {
			if (t2p->state != USED) continue;
			if (not_free(t2p->row, t2p->col, t2p->lev)) continue;
			if (t1p->type == t2p->type) matches++;
		};
	};

	i = XGameFont->ascent;
	j = XGameFont->descent;
	k = (200-(Y_COPY+i+j+1))/2 - (i+j)/2;
	sprintf(string, " Matches remaining = %3d ", matches);
	draw_string(string, 800, 200-k, XGameGC1);
	if (done_count == 0) {
		done_count = tiles_remaining;
		packet_send(GAME_DONE);
	};

	return(0);
}

event_quit() {

	packet_send(GAME_QUIT);
	exit(0);
}

event_tile() {
	int i, j, k = -1;
	Tile *tp;
	Tile *t0p = &tiles[0][0][5];
	Tile *t1p = &tiles[0][1][0];
	Tile *t2p = &tiles[0][2][0];
	Tile *t3p = &tiles[0][3][0];

	if (in_box(X_TILE, Y_TILE, COLS*TILE_WIDTH, ROWS*TILE_HEIGHT)) {
		for (k = LEVS-1; k >= 0; k--) {
			i = (y_coor - Y_TILE + (4*k))/TILE_WIDTH;
			j = (x_coor - X_TILE - (4*k))/TILE_WIDTH;
			if ((i < 0) || (i >= ROWS)) continue;
			if ((j < 0) || (j >= COLS)) continue;
			if (tiles[i][j][k].state == USED) break;
		};
	};

	if (in_box(t0p->x, t0p->y, TILE_WIDTH, TILE_HEIGHT)) {
		if (tiles[0][0][5].state == USED) { i = 0;  j = 0;  k = 5; };
	} else if (in_box(t1p->x, t1p->y, TILE_WIDTH, TILE_HEIGHT)) {
		if (tiles[0][1][0].state == USED) { i = 0;  j = 1;  k = 0; };
	} else if (in_box(t2p->x, t2p->y, TILE_WIDTH, TILE_HEIGHT)) {
		if (tiles[0][2][0].state == USED) { i = 0;  j = 2;  k = 0; };
	} else if (in_box(t3p->x, t3p->y, TILE_WIDTH, TILE_HEIGHT)) {
		if (tiles[0][3][0].state == USED) { i = 0;  j = 3;  k = 0; };
	};

	if (k >= 0) {
		play_tile(i, j, k);
	} else {
		XBell(XGameDisplay, 100);
		XFlush(XGameDisplay);
	};

	return(0);
}
7623!Funky!Stuff!
echo x - ./xmahjongg.mk
cat > ./xmahjongg.mk << '7623!Funky!Stuff!'
#
#*****************************************************************************
#									      *
#	Copyright (c) 1989 by Jeff S. Young.  All rights reserved under the   *
#	copyright laws of the United States.			      	      *
#									      *
#*****************************************************************************
#

#
#	makefile for xmahjongg
#
XINC	= /usr/include
XLIB	= /usr/lib/libX11.a
CFLAGS	= -O -I$(XINC)
BIN	= /usr/opus/jsy/bin
OBJS	= xmahjongg.o draw.o event.o initial.o packet.o play.o random.o tiles.o

all:	xmahjongg

xmahjongg: $(OBJS)
	   $(CC) $(CFLAGS) -o xmahjongg $(OBJS) $(XLIB)

install: all
	 cp xmahjongg $(BIN)
	 chmod 711 $(BIN)/xmahjongg
clean: 
	rm -f xmahjongg *.o

7623!Funky!Stuff!
echo x - ./draw.c
cat > ./draw.c << '7623!Funky!Stuff!'
/*
 ******************************************************************************
 *									      *
 *	Copyright (c) 1989 by Jeff S. Young.  All rights reserved under the   *
 *	copyright laws of the United States.			      	      *
 *									      *
 ******************************************************************************
 */

#include <stdio.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <sys/time.h>
#include "xmahjongg.h"

extern	int num_games;
extern	int done_count;
extern	int tiles_remaining;
extern	long seed;
extern	long buffer[];
extern	Uchar *tile_data[];
extern	Tile *tile1p, *tile2p;
extern	Tile tiles[ROWS][COLS][LEVS];
extern	Player player[];

extern	int BorderColor;
extern	int fore_color;
extern	int back_color;

extern	int screen;
extern	Font font;
extern  int XGameFD;
extern  GC XGameGC0;
extern  GC XGameGC1;
extern  Window XGameWindow;
extern  Display *XGameDisplay;
extern  XSizeHints XGameHints;
extern  XEvent XGameEvent;

long	*board_data;
long	*digit_data;
XImage	*XGameTileImage;
XImage	*XGameBoardImage;
XImage	*XGameCountImage;
XImage	*XGameLetterImage;
XImage	*XGameOptionImage;

#define	DRAW_NAME(pp) {							\
	int xpos = X_NAMES;						\
	int ypos = Y_NAMES + 15*(pp-player);				\
	sprintf((char *)buffer, "%8.8s", pp->name);			\
	draw_string((char *)buffer, xpos, ypos, XGameGC0);		\
}

#define	DRAW_BOARD(pp, pos) {						\
	sprintf((char *)buffer, "%5.5d", pp->board[pos]);		\
	draw_string((char *)buffer, X_BOARD+50*pos, Y_BOARD, XGameGC0);	\
}

#define DRAW_SCORE(pp, pos) {						\
	int score = pp->tiles[pos];					\
	if (score < 0) score *= -1;					\
	userGC = (pp->tiles[pos] > 0) ? XGameGC0 : XGameGC1;		\
	sprintf((char *)buffer, " %3.3d ", score);			\
	draw_string((char *)buffer, pp->x+50*pos, pp->y, userGC);	\
}

#define DRAW_TOTAL(pp, pos) {						\
	sprintf((char *)buffer, "TOTAL");				\
	draw_string((char *)buffer, X_BOARD+50*pos, Y_BOARD, XGameGC0);	\
	userGC = (pp->quit == 0) ? XGameGC0 : XGameGC1;			\
	sprintf((char *)buffer, " %3.3d ", pp->total);			\
	draw_string((char *)buffer, pp->x+50*pos, pp->y, userGC);	\
}

draw_user(pp, type)
Player *pp;
int type;
{
	int i;
	char buffer[20];
	GC userGC;

	if (type == GAME_START) {
		DRAW_NAME(pp);

		for (i = 0; i < MAX_BOARDS; i++) {
			if (pp->tiles[i] > TILES) break;
			DRAW_BOARD(pp, i);
			DRAW_SCORE(pp, i);
		};

		DRAW_TOTAL(pp, num_games);
	} else if (type == GAME_PLAY) {
		DRAW_BOARD(pp, pp->done);
		DRAW_SCORE(pp, pp->done);
		DRAW_TOTAL(pp, num_games);
	} else if (type == GAME_DONE) {
		DRAW_BOARD(pp, pp->done);
		DRAW_SCORE(pp, pp->done);
	} else if (type == GAME_QUIT) {
		DRAW_BOARD(pp, pp->done);
		DRAW_SCORE(pp, pp->done);
		DRAW_TOTAL(pp, num_games);
	} else {
		fprintf(stderr, "bad draw_user type (%d)\n", type);
		exit(1);
	};

	return(0);
}

draw_line(x0, y0, x1, y1) {

	XDrawLine(XGameDisplay, XGameWindow, XGameGC0, x0, y0, x1, y1);

	return(0);
}

draw_data() {
	int i, x, y;
	int w = TILE_WIDTH;
	int h = TILE_HEIGHT;

/*
 *	Set up the tile image holder
 */
	XGameTileImage = XCreateImage(
		XGameDisplay, DefaultVisual(XGameDisplay, screen),
		1, XYBitmap, 0, 0, TILE_WIDTH, TILE_HEIGHT, 8, 0);
        XGameTileImage->bitmap_bit_order = MSBFirst;
        XGameTileImage->byte_order = MSBFirst;

/*
 *	Display the tiles on the screen before the start of the game.  Order:
 *	dragons, seasons, plants, directions, circles, bamboos, ideographs.
 *	The 'if' statement below is just a way to figure out the x and y 
 *	coordinates of the tiles.
 *
 */
	for (i = 10; i <= 51; i++) {
		if ((10 <= i) && (i <= 12)) {
			x = 2*(i%9);
			y = 230;
		} else if ((13 <= i) && (i <= 24)) {
			x = (2*i + 7)%8;
			y = 310 + 80*((i-13)/4);
		} else if ((25 <= i) && (i <= 51)) {
			x = (i+2)%9;
			y = 310 + 80*((i+2)/9);
		};

		x = 42 + 106*x;
		XGameTileImage->data = (char *)tile_data[i];
		draw_image(XGameTileImage, x, y, w, h, XGameGC0);
	};

	return(0);
}

draw_tile(row, col)
int row, col;
{
	int i, k;
	GC imageGC;
	Tile *tp = NULL;
	Tile *up = NULL;

/*
 *	If this tile is already taken, don't draw it
 */
	if ((row == 0) && (col == 0)) {
		k = 5;
	} else if ((row == 0) && (col == 1)) {
		k = 0;
	} else if ((row == 0) && (col == 2)) {
		k = 0;
	} else if ((row == 0) && (col == 3)) {
		k = 0;
	} else  {
		for (k = 1; k < LEVS-1; k++) {
			tp = &tiles[row][col][k];
			if (tp->state != USED) break;
		};

		if (k-- == 1) return(0);
	};

	tp = &tiles[row][col][k];
	if (tp->state != USED) return(0);
        imageGC = (tp != tile1p) ? XGameGC0 : XGameGC1;

/*
 *	Draw the tile face
 */
	XGameTileImage->data = (char *)tp->data;
	draw_image(XGameTileImage, tp->x, tp->y,
		TILE_WIDTH, TILE_HEIGHT, imageGC);

	if ((k == 0) || (k == 5)) k = 1;

	for (i = 0; i < 4*k; i++) {
		draw_line(tp->x-i-1,tp->y+i+1,tp->x-i-1,tp->y+i+64);
		draw_line(tp->x-i-1,tp->y+i+64,tp->x-i+63,tp->y+i+64);
	};

	XFlush(XGameDisplay);

	return(0);
}

draw_count(data)
long *data;
{
	int digit, i, x, y;
	int count = (done_count != 0) ? done_count : tiles_remaining;

	if (XGameCountImage == 0) {
		digit_data = data;

		XGameCountImage = XCreateImage(
			XGameDisplay, DefaultVisual(XGameDisplay, screen),
			1, XYBitmap, 0, 0, TILE_WIDTH, TILE_HEIGHT, 8, 0);
        	XGameCountImage->bitmap_bit_order = MSBFirst;
        	XGameCountImage->byte_order = MSBFirst;
	};

	for (i = 0, x = 183, y = 100; i < 3; i++, x -= 64) {
		digit = count%10;
		/* adjust_data(buffer, digit_data+(128*digit), 128); */
		XGameCountImage->data = (char *)(digit_data+(128*digit));

		XPutImage(
			XGameDisplay, XGameWindow, XGameGC0, XGameCountImage,
			0, 0, x, y, TILE_WIDTH, TILE_HEIGHT);

		count /= 10;
	};

	XFlush(XGameDisplay);

	return(0);
}

draw_image(image, x, y, width, height, imageGC)
XImage *image;
int x, y;
int width, height;
GC imageGC;
{

	XPutImage(
		XGameDisplay, XGameWindow, imageGC,
		image, 0, 0, x, y, width, height);

	return(0);
}

draw_letter(data, x, y)
char *data;
int x, y;
{

	if (XGameLetterImage == 0) {
		XGameLetterImage = XCreateImage(
			XGameDisplay, DefaultVisual(XGameDisplay, screen),
			1, XYBitmap, 0, 0, LETTER_WIDTH, LETTER_HEIGHT, 8, 0);
        	XGameLetterImage->bitmap_bit_order = MSBFirst;
        	XGameLetterImage->byte_order = MSBFirst;
	};

	XGameLetterImage->data = data;
	XPutImage(
		XGameDisplay, XGameWindow, XGameGC1, XGameLetterImage,
		0, 0, x, y, LETTER_WIDTH, LETTER_HEIGHT);

	return(0);
}

draw_option(data, x, y, shade)
char *data;
int x, y, shade;
{
	GC optGC = (shade == 0) ? XGameGC0 : XGameGC1;

	if (XGameOptionImage == 0) {
		XGameOptionImage = XCreateImage(
			XGameDisplay, DefaultVisual(XGameDisplay, screen),
			1, XYBitmap, 0, 0, OPTION_WIDTH, OPTION_HEIGHT, 8, 0);
        	XGameOptionImage->bitmap_bit_order = MSBFirst;
        	XGameOptionImage->byte_order = MSBFirst;
	};

	XGameOptionImage->data = data;
	draw_image(XGameOptionImage, x, y, OPTION_WIDTH, OPTION_HEIGHT, optGC);
	XFlush(XGameDisplay);

	return(0);
}

draw_string(data, x, y, imageGC)
char *data;
int x, y;
GC imageGC;
{

	XDrawImageString(
		XGameDisplay, XGameWindow, imageGC,
		x, y, data, strlen(data));

	XFlush(XGameDisplay);

	return(0);
}
7623!Funky!Stuff!
echo x - ./xmahjongg.6
cat > ./xmahjongg.6 << '7623!Funky!Stuff!'
.\"	Copyright 1989 Jeff S. Young
.\"
.\"	Permission is given to copy and distribute for non-profit purposes.
.\"
.TH XMAHJONGG 6 "07 June 1989"
.SH NAME
xmahjongg \- X11R3 verion of the solitaire mahjongg game
.SH SYNOPSIS
.B xmahjongg
[ \fB-b \fI###\fR ] [ \fB-n \fI###\fR ] [ \fB-r\fR ] 
[ \fB-d \fIdisplay\fR ]
[ \fB-p \fIname@machine\fR ] [ \fI...\fR ] [ \fB-p \fIname@machine\fR ]
.SH DESCRIPTION
.I Mah jongg
is an ancient chinese game usually played by four players with tiles similar
to dominos.  This is an X windows version for the solitaire game originally
seen on the PC and later ported to SunView.  It also has a new tournament 
option.
.SH THEORY OF PLAY
The object of the game is to remove all the tiles from the board.  Tiles are
removed in by matching two identical tiles which have either an open left
edge or open right edge.  The only exception to this rule is that any open
"flower" tile (bamboo, orchid, plum, or chrysanthemum) matches any other 
open "flower" tile and any open "season" tile (spring, summer, autumn, or 
winter) matches any other open "season" tile.
.PP
The display has two distinct areas: the playing field and the control field.
.PP
The bottom area is the playing field.  To remove a pair of tiles, click a mouse
button on a tile (which will light up) and then click a mouse button on the 
matching tile.  At this point, both tiles will disappear from the board.  If 
after selecting the first tile, you decide that you don't wish to play that 
tile, simply reclick the button on the tile.
.PP
The top area is the control field.  The board number and the number of remaining
tiles are on the left side of the board.  The right side has some options 
for controlling the game.  To select an option, click a mouse button on it.
\fI      SAME \fR
Start the same game again.  This option is disabled in tournament mode.
\fI      NEW \fR
Start a new game.
\fI      DONE \fR
Check to see if you missed any matches.  When you think that you don't have any
more matches left, clicking "DONE" will tell you the number of matches which 
you missed.  If you missed some, you can continue play, but your score will
not change.  The "DONE" field stays high-lighted to show that you have already 
tried this option.
\fI      QUIT \fR
Exit the game.
.SH OPTIONS
.IP \fB\-b \fR
Start the game with board number ###. Board numbers range from 1 to 99999.
.IP \fB\-d \fR
Use the given display name instead of the current default.
.IP \fB\-n \fR
Number of games to play in tournament mode.  See below.
.IP \fB\-r\fR
Reverse video mode.
.IP \fB\-p \fR
Set up a tournament with the specified player on the specified machine.
.SH TOURNAMENT MODE
Using the [  \fI -p \fR ] parameter starts a tournament.  In this mode
several players can compete on a series of boards.  The players should agree
in advance on a starting board number.  The default tournament is three games,
although this can be changed with the [ \fI -n \fR ] parameter.  If another
player clicks "DONE" then, their score will be highlighted for that game.
The tournament winner is the player with the lowest total score over the series.
.br
   
.br
Example:
.br
Three users ('abc' on 'xyzzy', 'def' on 'plugh', and 'ghi' on 'plover') wish
to play a 5 game tournament starting with board 12345.
Here are their command lines:
.br
   
.br
      'abc' types:    xmahjongg -b 12345 -n 5 -p def@plugh -p ghi@plover
.br
      'def' types:    xmahjongg -b 12345 -n 5 -p abc@xyzzy -p ghi@plover
.br
      'ghi' types:    xmahjongg -b 12345 -n 5 -p def@plugh -p abc@xyzzy
.br
   
.br
Note that the players can be in any order on the command line and that the
user does not list his/her own name on the command line.
.SH FILES
/usr/games/xmahjongg			executable
.SH AUTHOR
Copyright 1989 Jeff S. Young
.br
<jsy@cray.com>
.br

.br
The tiles themselves are copyright 1988 by Mark A. Holm 
<tektronix!tessi!exc!markh>.  His copyright notice is included in the 
source code.
.SH BUGS
Doesn't use the Xdefault information.
.PP
No permanent score file.
.PP
You cannot have the same user name for two different players in a tournament.
.PP
Uses sockets for tournament mode.
.PP
No color xmahjongg (yet).
7623!Funky!Stuff!
echo x - ./play.c
cat > ./play.c << '7623!Funky!Stuff!'
/*
 ******************************************************************************
 *									      *
 *	Copyright (c) 1989 by Jeff S. Young.  All rights reserved under the   *
 *	copyright laws of the United States.			      	      *
 *									      *
 ******************************************************************************
 */

#include <stdio.h>
#include <sys/time.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include "xmahjongg.h"
#include "tilenames.h"

extern	int done_count;
extern	int tourn_flag;
extern	int keep_playing;
extern	int tiles_remaining;
extern	long mask[];
extern	long buffer[];
extern	Uchar *tile_data[];
extern	Tile tiles[ROWS][COLS][LEVS];

extern	int screen;
extern	int fore_color;
extern	int back_color;
extern	int BorderColor;

extern	Font font;
extern	int XGameFD;
extern	GC XGameGC0;
extern	GC XGameGC1;
extern	Window XGameWindow;
extern	Display *XGameDisplay;
extern	XSizeHints XGameHints;
extern	XEvent XGameEvent;
extern	XFontStruct *XGameFont;
extern	XImage *XGameTileImage;

extern	Tile *tile1p;
extern	Tile *tile2p;

long	alinger;
Uchar	filler[1024];

XImage	*XGameTileImage0;
XImage	*XGameTileImage1;
XImage	*XGameTileImage2;
XImage	*XGameTileImage3;
XImage	*XGameTileImage4;

play_tile(row, col, lev)
int row, col, lev;
{
	Tile *tp = &tiles[row][col][lev];

	if (not_free(row, col, lev)) {
		XBell(XGameDisplay, 100);
		XFlush(XGameDisplay);
	} else if (tile1p == NULL) {
		invert_tile(tp, 1);
		tile1p = tp;
	} else if (tp == tile1p) {
		invert_tile(tp, 0);
		tile1p = NULL;
	} else if (tp->type == tile1p->type) {
		/* if (tiles_remaining == 2) keep_playing = 0; */
		tiles_remaining -= 2;
		remove_tile(tile1p);
		tile1p = NULL;
		remove_tile(tp);
		tp = NULL;
		draw_count(tiles_remaining);
		if (done_count == 0) packet_send(GAME_PLAY);
	} else {
		XBell(XGameDisplay, 100);
		XFlush(XGameDisplay);
	};

	return(0);
}

invert_tile(tp, type)
Tile *tp;
int type;
{
	int i;
	int w = TILE_WIDTH;
	int h = TILE_HEIGHT;
	GC imageGC = (type == 0) ? XGameGC0 : XGameGC1;

/*
 *	Do the special tiles here, else call the general purpose routine
 */
	if (tp->row == 0) {
		XGameTileImage->data = (char *)tp->data;
		draw_image(XGameTileImage, tp->x, tp->y, w, h, imageGC);
		XFlush(XGameDisplay);
	} else {
		redraw_tile(tp, type);
		XFlush(XGameDisplay);
	};

	return(0);
}

remove_tile(tp)
Tile *tp;
{
int i;
int x, y;
char *cp;
long *bp;
long *lp1, *lp2, *lp3, *lp4;

/*
 *	Do the special tiles here, else call the general purpose routine
 */
	if ((tp->row == 0) && (tp->col == 0)) {		/* top tile */
		for (i = 0; i < 2048; i++) {
			buffer[i] = 0L;
		};

		if (XGameTileImage0 == 0) {
			XGameTileImage0 = XCreateImage(
				XGameDisplay, 
				DefaultVisual(XGameDisplay, screen),
				1, XYBitmap, 0, 0, 
				2*TILE_WIDTH, 2*TILE_WIDTH, 8, 0);

			XGameTileImage0->bitmap_bit_order = MSBFirst;
			XGameTileImage0->byte_order = MSBFirst;
			XGameTileImage0->data = (char *)buffer;
		};

		tp->state = FREE;

		lp1 = (long *)tiles[4][6][4].data;
		lp2 = (long *)tiles[4][7][4].data;
		lp3 = (long *)tiles[5][6][4].data;
		lp4 = (long *)tiles[5][7][4].data;
		bp = buffer;

		for (i = 0; i < 4*64; i += 4) {
			*(bp+i+000) = *(lp1 + 0);
			*(bp+i+001) = *(lp1 + 1);
			*(bp+i+002) = *(lp2 + 0);
			*(bp+i+003) = *(lp2 + 1);
			*(bp+i+256) = *(lp3 + 0);
			*(bp+i+257) = *(lp3 + 1);
			*(bp+i+258) = *(lp4 + 0);
			*(bp+i+259) = *(lp4 + 1);
			lp1 += 2;
			lp2 += 2;
			lp3 += 2;
			lp4 += 2;
		};

		draw_image(XGameTileImage0, 
			tiles[4][6][4].x, tiles[4][6][4].y,
			2*TILE_WIDTH, 2*TILE_HEIGHT, XGameGC0);

	} else if ((tp->row == 0) && (tp->col == 1)) {	/* left tile */
		for (i = 0; i < 2048; i++) {
			buffer[i] = 0L;
		};

		if (XGameTileImage1 == 0) {
			XGameTileImage1 = XCreateImage(
				XGameDisplay, 
				DefaultVisual(XGameDisplay, screen),
				1, XYBitmap, 0, 0, 
				TILE_WIDTH+16, TILE_WIDTH+4, 8, 0);

			XGameTileImage1->bitmap_bit_order = MSBFirst;
			XGameTileImage1->byte_order = MSBFirst;
			XGameTileImage1->data = (char *)buffer;
		};

		tp->state = FREE;

		draw_image(XGameTileImage1, 
			tp->x-16, tp->y,
			TILE_WIDTH+16, TILE_HEIGHT+4, XGameGC0);

		for (i = 60; i < 64; i++) {
			draw_line(tp->x + i, tp->y, tp->x + i, tp->y + 68);
		};

	} else if ((tp->row == 0) && (tp->col == 2)) {	/* middle tile */
		for (i = 0; i < 2048; i++) {
			buffer[i] = 0L;
		};

		if (XGameTileImage2 == 0) {
			XGameTileImage2 = XCreateImage(
				XGameDisplay, 
				DefaultVisual(XGameDisplay, screen),
				1, XYBitmap, 0, 0, 
				TILE_WIDTH, TILE_WIDTH+4, 8, 0);

			XGameTileImage2->bitmap_bit_order = MSBFirst;
			XGameTileImage2->byte_order = MSBFirst;
			XGameTileImage2->data = (char *)buffer;
		};

		tp->state = FREE;

		draw_image(XGameTileImage2, 
			tp->x, tp->y,
			TILE_WIDTH, TILE_HEIGHT+4, XGameGC0);

	} else if ((tp->row == 0) && (tp->col == 3)) {	/* right tile */
		for (i = 0; i < 2048; i++) {
			buffer[i] = 0L;
		};

		if (XGameTileImage3 == 0) {
			XGameTileImage3 = XCreateImage(
				XGameDisplay, 
				DefaultVisual(XGameDisplay, screen),
				1, XYBitmap, 0, 0, 
				TILE_WIDTH, TILE_WIDTH+4, 8, 0);

			XGameTileImage3->bitmap_bit_order = MSBFirst;
			XGameTileImage3->byte_order = MSBFirst;
			XGameTileImage3->data = (char *)buffer;
		};

		tp->state = FREE;

		draw_image(XGameTileImage3, 
			tp->x, tp->y,
			TILE_WIDTH, TILE_HEIGHT+4, XGameGC0);

		XClearArea(XGameDisplay, XGameWindow,
			tp->x - 4, tp->y + 64, 4, 4, False);

		x = tp->x;
		y = tp->y + 63;
		for (i = 1; i <= 4; i++) {
			draw_line(x-4, y+i, x-i, y+i);
		};

	} else {
		tp->state = FREE;
		redraw_tile(tp, 0);
	};

	XFlush(XGameDisplay);

	return(0);
}

redraw_tile(tp, type)
Tile *tp;
int type;
{
	int i, j, k;
	int row = tp->row;
	int col = tp->col;
	int lev = tp->lev;
	int bl = 16+4*lev;
	int br = 32 - bl;
	int save0, save1, save2;
	int save3, save4, save5;
	int w_off = 641 - 32*lev;
	long *bp = &buffer[w_off];
	Uchar *cp;

/*
 *	Set up the tile image
 */
	if (XGameTileImage4 == 0) {
		XGameTileImage4 = XCreateImage(
			XGameDisplay, DefaultVisual(XGameDisplay, screen),
			1, XYBitmap, 0, 0, TILE_WIDTH+32, TILE_WIDTH+4, 8, 0);

		XGameTileImage4->bitmap_bit_order = MSBFirst;
		XGameTileImage4->byte_order = MSBFirst;
		XGameTileImage4->data = (char *)filler;
	};

/*
 *	Zero out the buffer before putting the data in it.
 */
	for (i = 0; i < 2048; i++) {
		buffer[i] = 0;
	};

/*
 *	Loop over all tiles in the area.  Redraw the screen area with the 
 *	image of the tile.  If this is the tile in the middle, then the
 *	option type has the following effect:
 *		0)  draw the tile normally
 *		1)  draw the tile inverted
 */
	for (j = col+1; j >= col-1; j--) {
		for (i = row-1; i <= row+1; i++) {
			place_image(row, col, i, j, type);
		};
	};

/*
 *	Justify the data in the buffer for display purposes
 */
	for (i = 0, j = 0; i < 68; i++, bp += 8) {
		buffer[j++] =  (*(bp+0) << bl) | ((*(bp+1) >> br) & mask[bl]);
		buffer[j++] =  (*(bp+1) << bl) | ((*(bp+2) >> br) & mask[bl]);
		buffer[j++] =  (*(bp+2) << bl) | ((*(bp+3) >> br) & mask[bl]);

	};

	for (j = 0, cp = filler; j < 3*68; j++) {
		*cp++ = (buffer[j] >> 24) & 0xff;
		*cp++ = (buffer[j] >> 16) & 0xff;
		*cp++ = (buffer[j] >>  8) & 0xff;
		*cp++ = (buffer[j] >>  0) & 0xff;
	};

/*
 *	Display the data
 */
	draw_image(XGameTileImage4, tp->x - 16, tp->y,
		TILE_WIDTH+32, TILE_HEIGHT+4, XGameGC0);

	XFlush(XGameDisplay);

	return(0);
}

place_image(base_row, base_col, row, col, type)
int base_row, base_col, row, col, type;
{
	int k;

	if ((row == 0) || (row == ROWS)) return(0);
	if ((col == 0) || (col == COLS)) return(0);

	for (k = LEVS-2; k > 0; k--) {
		if (tiles[row][col][k].state ==USED) break;
	};

	if (type == 2) k--;
	if (k > 0) copy_image(&tiles[row][col][k], base_row, base_col, type);
		
	return(0);
}

copy_image(tp, x, y, type)
Tile *tp;
int x, y;
int type;
{
	int i, j;
	int m_off = 0;
	int b_off = 4*tp->lev;
	int w_off = 642 + 512*(tp->row - x) + 2*(tp->col - y) - 32*tp->lev;

	int s1 = b_off;
	int s2 = 32 - b_off;
	int m0 = mask[32];
	int m1 = mask[s2];
	int m2 = mask[s1] << s2;
	int lines = 4*tp->lev;
	unsigned char *cp = tp->data;
	long long0, long1;
	long *bp = &buffer[w_off];

/*
 *	Copy the tile into the buffer.  The following kludge is to get around
 *	an anomaly in some of the 'C' compilers.
 */
	for (i = 0, j = 0; i < 64; i++) {
		long0 = ((*(cp+0) << 24) & 0xff000000) |
		        ((*(cp+1) << 16) & 0x00ff0000) |
		        ((*(cp+2) <<  8) & 0x0000ff00) |
		        ((*(cp+3) <<  0) & 0x000000ff);
		long1 = ((*(cp+4) << 24) & 0xff000000) |
		        ((*(cp+5) << 16) & 0x00ff0000) |
		        ((*(cp+6) <<  8) & 0x0000ff00) |
		        ((*(cp+7) <<  0) & 0x000000ff);
		cp += 8;

		*(bp+j+0) = (*(bp+j+0) & m2) | ((long0 >> s1) & m1);
		*(bp+j+1) = ((long0 << s2) & m2) | ((long1 >> s1) & m1);
		*(bp+j+2) = (*(bp+j+2) & m1) | ((long1 << s2) & m2);
		j += 8;
	};

/*
 *	If this is the center tile, then invert it if type=1
 */
	if ((tp->row == x) && (tp->col == y) && (type == 1)) {
		for (i = 0, j = 0, bp = &buffer[w_off]; i < 64; i++) {
			*(bp+j+0) ^= m1;
			*(bp+j+1) ^= m0;
			*(bp+j+2) ^= m2;
			j += 8;
		};
	};

/*
 *	Copy the border into the buffer
 */
	for (i = 1; i <= lines; i++) {
		b_off--;  w_off += 8;  m_off = 1 << (31-b_off);
		for (j = w_off; j < w_off+512; j += 8) {
			buffer[j] |= m_off;
		};

		buffer[w_off+504] = mask[32-b_off];
		buffer[w_off+505] = 0xffffffff;
		buffer[w_off+506] |= mask[b_off] << (32-b_off);
	};

	return(0);
}

not_free(row, col, lev) 
int row, col, lev;
{
	int rc = 0;

	if (lev == 4) {
		if (tiles[0][0][5].state == USED) rc = 1;
	} else if ((row == 0) && (col == 2) && (lev == 0)) {
		if (tiles[0][3][0].state == USED) rc = 1;
	} else if ((row == 4) && (col == 1) && (lev == 1)) {
		if (tiles[0][1][0].state == USED) rc = 1;
	} else if ((row == 5) && (col == 1) && (lev == 1)) {
		if (tiles[0][1][0].state == USED) rc = 1;
	} else if ((row == 4) && (col == 12) && (lev == 1)) {
		if (tiles[0][2][0].state == USED) rc = 1;
	} else if ((row == 5) && (col == 12) && (lev == 1)) {
		if (tiles[0][2][0].state == USED) rc = 1;
	} else if (row == 0) {
		rc = 0;
	} else if ((col == 1) || (col == 12)) {
		rc = 0;
	} else if ((1 < col) && (col < 12)) {
		if (tiles[row][col][lev+1].state == USED) {
			rc = 1;
		} else if (tiles[row][col-1][lev].state != USED) {
			rc = 0;
		} else if (tiles[row][col+1][lev].state != USED) {
			rc = 0;
		} else {
			rc = 1;
		};
	};

	return(rc);
}
7623!Funky!Stuff!
echo x - ./packet.c
cat > ./packet.c << '7623!Funky!Stuff!'
/*
 ******************************************************************************
 *									      *
 *	Copyright (c) 1989 by Jeff S. Young.  All rights reserved under the   *
 *	copyright laws of the United States.			      	      *
 *									      *
 ******************************************************************************
 */

#include <stdio.h>
#include <signal.h>
#include <sys/time.h>
#include <sys/types.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <netinet/in.h>
#include "xmahjongg.h"

extern	int errno;

extern	int maxfds;
extern	int playfds;
extern	int done_count;
extern	int tourn_flag;
extern	int num_games;
extern	int num_players;
extern	int tiles_remaining;
extern	long seed;
extern	long buffer[];
extern	long game_board[];
extern	Uchar *tile_data[];
extern	Tile *tile1p, *tile2p;
extern	Tile tiles[ROWS][COLS][LEVS];
extern	Player player[MAX_PLAYERS];
extern	Player *pp, *mypp;

extern	int screen;
extern	int fore_color;
extern	int back_color;
extern	int BorderColor;

extern	Font font;
extern	int XGameFD;
extern	GC XGameGC0;
extern	GC XGameGC1;
extern	Window XGameWindow;
extern	Display *XGameDisplay;
extern	XSizeHints XGameHints;
extern	XEvent XGameEvent;
extern	XFontStruct *XGameFont;

int	x_coor;
int	y_coor;
Packet	packet;

packet_send(type)
int type;
{
	int i;
	int packetlen = sizeof(Packet);
	char ascii[24];

	if (tourn_flag == 0) return(0);
	i = mypp->done;

/*
 *	Update the information on my board
 */
	if (type == GAME_START) {
		mypp->done++;
		mypp->tiles[mypp->done] = TILES;
		mypp->board[mypp->done] = seed;
		if ((i >= 0) && (mypp->tiles[i] > 0)) mypp->tiles[i] *= -1;
		draw_user(mypp, GAME_START);
	} else if (type == GAME_PLAY) {
		mypp->total -= 2;
		mypp->tiles[i] = tiles_remaining;
		draw_user(mypp, GAME_PLAY);
	} else if (type == GAME_DONE) {
		if (mypp->tiles[i] > 0) mypp->tiles[i] *= -1;
		draw_user(mypp, GAME_DONE);
	} else if (type == GAME_QUIT) {
		draw_user(mypp, GAME_QUIT);
	} else {
		fprintf(stderr, "bad packet type = %d\n", type);
		exit(1);
	};

/*
 *	Send the information to the other players
 */
	packet.type = htons(type);
	packet.port = htons(mypp->port);
	packet.tiles = htons(tiles_remaining);
	packet.board = htonl(seed);
	strcpy(packet.name, mypp->name);

	for (i = 0, pp = player; i < num_players; i++, pp++) {
		if ((pp->fd < 0) || (pp->type == 'M')) continue;
		if (write(pp->fd, (char *)&packet, packetlen) != packetlen) {
			playfds ^= (1 << pp->fd);
			close(pp->fd);
			pp->fd = -1;
		};
	};

	return(0);
}

packet_recv(fd)
int fd;
{
	int i;
	int readfds = 1 << fd;
	int packetlen = sizeof(Packet);
	struct timeval timeout;
	char ascii[32];
	char *cp;

/*
 *	Read in the packet which is waiting for us.
 */
	errno = 0;
	timeout.tv_sec = 0L;
	timeout.tv_usec = 0L;

	if (select(maxfds, &readfds, NULL, NULL, &timeout) <= 0) {
		return(0);
	} else if (read(fd, (char *)&packet, packetlen) != packetlen) {
		playfds ^= (1 << pp->fd);
		close(pp->fd);
		pp->fd = -1;
		return(0);
	};

/*
 *	Find this players structure.
 */
	packet.type = ntohs(packet.type);
	packet.port = ntohs(packet.port);
	packet.tiles = ntohs(packet.tiles);
	packet.board = ntohl(packet.board);

	for (i = 0, pp = player; i < num_players; i++, pp++) {
		if (pp->port == packet.port) break;
	};

	if (i == num_players) {
		fprintf(stderr, "****** BAD PACKET ******\n");
		fprintf(stderr, "packet name = %s   port=%d\n",
			packet.name, packet.port);
		return(0);
	};

	i = pp->done;

	switch (packet.type) {
		case GAME_START:
			pp->done++;
			pp->board[i+1] = packet.board;
			pp->tiles[i+1] = packet.tiles;
			if ((i >= 0) && (pp->tiles[i] > 0)) pp->tiles[i] *= -1;
			draw_user(pp, GAME_START);
			break;
		case GAME_PLAY:
			pp->total -= 2;
			pp->tiles[i] = packet.tiles;
			draw_user(pp, GAME_PLAY);
			break;
		case GAME_DONE:
			if (pp->tiles[i] > 0) pp->tiles[i] *= -1;
			draw_user(pp, GAME_DONE);
			break;
		case GAME_QUIT:
			pp->tiles[i] = -packet.tiles;
			pp->quit = 1;
			draw_user(pp, GAME_QUIT);
			playfds ^= (1 << pp->fd);
			close(pp->fd);
			pp->fd = -1;
			break;
		default:
			fprintf(stderr, "bad switch (%d)\n", packet.type);
			exit(1);
			break;
	};

	return(0);
}
7623!Funky!Stuff!