#include <stdio.h>
#include <string.h>
#include <time.h>
#include <ctype.h>

#define PFSIZE (22*40)

extern unsigned long rand( void );

unsigned char playfield[ PFSIZE ] = {
"#####################   Legende:        "
"#.........#.........#   @ - DU          "
"#.........#.........#   L - Lebender    "
"#.........#.........#   Z - Zombie      "
"#.........#.........#   O - Oberzombie  "
"#.........#.........#   T - Treuhänder  "
"#.........#.........#   ################"
"#.........#.........#####..............#"
"########..#...#######..................#"
"#......#..................#########....#"
"#.........###########..........#.......#"
"########..#.........#..........#.......#"
"#.........#.........#..........#.......#"
"########............#..........#.......#"
"#.........#.........#..........#.......#"
"########..###########..........#.......#"
"#......#..#.........####################"
"#...####..#.........#                   "
"#...................#                   "
"#..................##                   "
"#####^###############                   "
};

unsigned char pf2[ PFSIZE ];

int my_x, my_y;
int oz_x, oz_y, ostate;
int th_x, th_y, tstate;
int p_x[ 10 ], p_y[ 10 ], pstate[ 10 ];
int step, lives, nextmiete;

#define PF(x,y) playfield[(y)*40+(x)]

#define RND(x) (rand()%(x))

//
//	Arg-String-Format (alles Hex)
//  SSSSSSLMXMYOXOYOTXTYT[PXPYP]*10
//  Step
//

static void makeparmstring( int mx, int my )
{
	int c;

	printf( "%06x%01x", step + 1, lives );
	printf( "%02x%02x", my_x + mx, my_y + my );
	printf( "%02x%02x", oz_x, oz_y );
	printf( "%02x%02x", th_x, th_y );
	printf( "%01x", tstate );
	printf( "%01x", ostate );

	for( c = 0; c < 10; c++ )
	{
		printf( "%02x%02x%01x", p_x[ c ], p_y[ c ], pstate[ c ] );
	}
}

static void printpf( void )
{
	int x, y;
	int c;
	int zc = 0;

	PF(my_x,my_y) = '@';
	PF(oz_x,oz_y) = 'O';
	PF(th_x,th_y) = ( tstate & 1 ) ? 't' : 'T';

	for( c = 0; c < 10; c++ )
	{
		PF( p_x[ c ], p_y[ c ] ) = ( pstate[ c ] & 1 ) ? 'Z' : 'L';
		if( pstate[ c ] & 1 )
			zc++;
	}

	printf( "Spielzug Nr. <B>%d</B>\n...Mieten: <B>%d</B>...Nächste Miete fällig in <B>%d</B> Zügen", step, lives, nextmiete );
	printf( "<BR>Zombies <B>%d</B>, Lebende <B>%d</B> - Treuhänder ist <B>%s</B>\n",
		zc, 10 - zc, ( tstate & 1 ) ? "verzombt" : "lebend"
	);
	printf( "<PRE>" );

	for( y = 0; y < 21; y++ )
	{
		int x;
		for( x = 0; x < 40; x++ )
		{
			int ch = playfield[ y * 40 + x ];
			if( ch == '.' || ch == '#' || ch == ' ' )
				putchar( ch );
			else
				printf( "<B>%c</B>", ch );
		}
        printf( "\n" );
	}
	printf( "</PRE>" );

	printf( "Mögliche Richtungen: " );
	if( PF(my_x-1,my_y) == '.' )
	{
		printf( "[<A HREF=\"/cgi-bin/zombie?" );
		makeparmstring( -1, 0 );
		printf( "\">LINKS</A>]" );
	}
	if( PF(my_x+1,my_y) == '.' )
	{
		printf( "[<A HREF=\"/cgi-bin/zombie?" );
		makeparmstring( 1, 0 );
		printf( "\">RECHTS</A>]" );
	}
	if( PF(my_x,my_y+1) == '.' )
	{
		printf( "[<A HREF=\"/cgi-bin/zombie?" );
		makeparmstring( 0, 1 );
		printf( "\">RUNTER</A>]" );
	}
	if( PF(my_x,my_y-1) == '.' )
	{
		printf( "[<A HREF=\"/cgi-bin/zombie?" );
		makeparmstring( 0, -1 );
		printf( "\">HOCH</A>]" );
	}
	printf( "[<A HREF=\"/cgi-bin/zombie?" );
	makeparmstring( 0, 0 );
	printf( "\">WARTEN</A>]" );
	printf( "<BR>" );
}

static void initgame( void )
{
	int c;

	my_x = 4;
	my_y = 3;
	oz_x = 28;
	oz_y = 15;
	th_x = 12;
	th_y = 5;
	tstate = 1;

	PF(my_x,my_y) = '@';
	PF(oz_x,oz_y) = 'O';
	PF(th_x,th_y) = ( tstate & 1 ) ? 't' : 'T';

	for( c = 0; c < 10; c++ )
	{
		int xp, yp;
		do
		{
			xp = RND(40);
			yp = RND(21);		
		}
		while( PF(xp,yp)!='.' );
		p_x[ c ] = xp;
		p_y[ c ] = yp;
		PF(xp,yp)='!';

		if( c > 2 )
			pstate[ c ] |= 1;
	}

	step = 1;
	lives = 3;

	nextmiete = 29 - ( step % 30 );

	printpf();
}

static void gethex( char *s, int offs, int len, int *to )
{
	int hv = 0;

	s += offs;

	while( len-- )
	{
		int dig = 0;

		if( isdigit( *s ) )
			dig = *s - '0';
		else if( *s >= 'a' && *s <='f')
			dig = ( *s - 'a' ) + 10;
		else if( *s >= 'A' && *s <='F')
			dig = ( *s - 'a' ) + 10;

		hv <<= 4;
		hv += dig;
		s++;
	}
	*to = hv;
}

static void parsearg( char *s )
{
	int c;
	gethex( s, 0, 6, &step );
	gethex( s, 6, 1, &lives );
	gethex( s, 7, 2, &my_x );
	gethex( s, 9, 2, &my_y );
	gethex( s, 11, 2, &oz_x );
	gethex( s, 13, 2, &oz_y );
	gethex( s, 15, 2, &th_x );
	gethex( s, 17, 2, &th_y );
	gethex( s, 19, 1, &tstate );
	gethex( s, 20, 1, &ostate );
	for( c = 0; c < 10; c++ )
	{
		gethex( s, 21 + c*5, 2, &p_x[ c ] );
		gethex( s, 23 + c*5, 2, &p_y[ c ] );
		gethex( s, 25 + c*5, 1, &pstate[ c ] );
	}
}

static void movep( int *xpp, int *ypp, int *dir )
{
	int xp, yp;
	int e, d;

	d = ( ( *dir ) >> 1 ) & 3;

	if( RND(25)==12 )
		d = RND(4);

	for( e = 0; e < 4; e++, d += RND(35) )
	{
		xp = *xpp;
		yp = *ypp;
		switch( d % 4 )
		{
			case 0:
				xp--;
				break;

			case 1:
				xp++;
				break;

			case 2:
				yp--;
				break;

			case 3:
				yp++;
				break;
		}

		if( PF(xp,yp)=='.')
			break;
	}
	if( e < 4 )
	{
		*xpp = xp;
		*ypp = yp;
		*dir &= ~(3<<1);
		*dir |= (d%4)<<1;
	}
}

static int testfor( int xp, int yp, int what )
{
	int f = 0;
	int d1, d2;

	for( d1 = -1; d1 < 2; d1++ )
		for( d2 = -1; d2 < 2; d2++ )
			if( PF( xp + d1, yp + d2 ) == what )
				f++;
	return( f );
}

static void domiete( void )
{
	int c;

	printf( "<HR><H1>Die Miete ist fällig!</H1><HR>\n" );

	printf( "Es ist 'mal wieder so weit: Die Miete ist fällig, und wie üblich hat\n" );
	printf( "einer der Zombies sein ganzes Geld verkifft.<P>\n" );

	if( tstate & 1 )
	{
		printf( "Leider ist der Mietkonto-Treuhänder auch verzombt und läßt sich\n" );
		printf( "von den übrigen Zombies belabern, den Pleitezombie nicht vor die Tür zu\n" );
		printf( "setzen.<P>\n" );
		printf( "<H3><B>Du</B> musst für den Pleitezombie bezahlen...\</H3>\n" );

		if( !lives-- )
		{
			printf( "<HR><BR>Leider bist Du nun genau so pleite wie der Zombie,\n" );
			printf( "womit das Spiel für Dich <I>gelaufen</I> ist.\n<P>" );
			printf( "Tröste Dich, Du hast immerhin %d Züge lang gegen die Zombies\n", step );
			printf( "durchgehalten.\n" );
			return;
		}

		printf( "<HR>Du hast jetzt nur noch %d weitere Chancen, bist Du auch Pleite bist...\n<P>", lives );

		printf( "<A HREF=\"/cgi-bin/zombie?" );
		makeparmstring( 0, 0 );
		printf( "\">[WEITER im Spiel]</A>" );
		return;
	}

	for( c = 0; c < 10; c++ )
	{
		if( pstate[ c ] & 1 )
		{
			pstate[ c ] &= ~1;
			break;
		}
	}

	printf( "Mit vereinten Kräften wird er vor die Tür gesetzt und durch einen Lebenden ersetzt!\n<P>" );
	printf( "<A HREF=\"/cgi-bin/zombie?" );
	makeparmstring( 0, 0 );
	printf( "\">[WEITER im Spiel]</A>" );
}

static void dowin( void )
{
	printf( "<HR><H1>Gewonnen!</H1><HR>\n" );

	printf( "Du hast es in %d Schritten geschafft, die WG zombiefrei\n", step );
	printf( "zu kriegen!\n" );
	switch( RND( 3 ) )
	{
		case 0:
			printf( "Der Oberzombie pfeift sich vor lauter\n" );
			printf( "Frustration eine Überdosis Marusha rein und verbringt\n" );
			printf( "den Rest seiner Tage in der Klapsmühle, wo er denkt,\n" );
			printf( "er wäre ein Baum..." );
			break;

		case 1:
			printf( "Drei Tage später beobachtest Du den Oberzombie,\n" );
			printf( "wie er die Katzen füttert. Offenbar wird auch er\n" );
			printf( "langsam wieder normal.\n" );
			break;

		case 2:
			printf( "Der Oberzombie zieht in eine Sekten-Kommune um\n" );
			printf( "und intoniert den ganzen Tag nur nach sein persönliches\n" );
			printf( "Mantra, das ihm Außerirdische mitgeteilt haben." );
			break;
	}
	printf( "<HR><H2>Gratulation!</H2>" );
	return;
}

static void dogame( char *arg )
{
	int c;
	int zc = 0;

	parsearg( arg );

	nextmiete = 29 - ( step % 30 );

	if( !nextmiete )
	{
		domiete();
		return;
	}

	// Nun Player setzen
	PF(my_x,my_y) = '@';
	PF(oz_x,oz_y) = 'O';
	PF(th_x,th_y) = ( tstate & 1 ) ? 't' : 'T';

	for( c = 0; c < 10; c++ )
	{
		PF( p_x[ c ], p_y[ c ] ) = ( pstate[ c ] & 1 ) ? 'Z' : 'L';
	}

	// Player bewegen
	for( c = 0; c < 10; c++ )
	{
		PF( p_x[ c ], p_y[ c ] ) = '.';
		if( testfor( p_x[ c ], p_y[ c ], 'Z' ) )
			pstate[ c ] |= 1;
		if( testfor( p_x[ c ], p_y[ c ], 'L' ) > 1 )
			pstate[ c ] &= ~1;
		if( testfor( p_x[ c ], p_y[ c ], 'O' ) )
			pstate[ c ] |= 1;
		if( testfor( p_x[ c ], p_y[ c ], '@' ) )
			pstate[ c ] &= ~1;
		movep( &p_x[ c ], &p_y[ c ], &pstate[ c ] );
		PF( p_x[ c ], p_y[ c ] ) = ( pstate[ c ] & 1 ) ? 'Z' : 'L';

		if( pstate[ c ] & 1 )
			zc++;

	}
	movep( &oz_x, &oz_y, &ostate );
	if( testfor( th_x, th_y, 'O' ) || testfor( th_x, th_y, 'Z' ) )
		tstate |= 1;
	if( testfor( th_x, th_y, '@' ) )
		tstate &= ~1;
	movep( &th_x, &th_y, &tstate );

	memcpy( playfield, pf2, PFSIZE );

	if( !zc )
	{
		dowin();
		return;
	}

	printpf();
}

int main( int argc, char **argv )
{
	srand( time( 0 ) );
	memcpy( pf2, playfield, PFSIZE );

	printf( "Content-type: text/html\n\n" );
	printf( "<TITLE>FIGHT THE ZOMBIES</TITLE><HEAD><H1>Fight The Zombies</H1></HEAD>\n<BODY>" );

	if( ( argc == 1 ) || ( strlen( argv[ 1 ] ) != 71 ) )
		initgame();
	else
		dogame( argv[ 1 ] );

	printf( "<HR><I>Fight the Zombies</I> 1.0 nonsensed by <A HREF=\"http://www.pluribus.wupper.de/~owagner\">Oliver Wagner</A>" );
	printf( "\n</BODY>\n" );
}
