xref: /plan9-contrib/sys/src/games/juggle.c (revision 5000fddee1108b517ce6ed55f4cb5000080fb70b)
18bfd7700SDavid du Colombier #include <u.h>
28bfd7700SDavid du Colombier #include <libc.h>
38bfd7700SDavid du Colombier #include <draw.h>
48bfd7700SDavid du Colombier #include <event.h>
55ca9eac5SDavid du Colombier 
65ca9eac5SDavid du Colombier enum
75ca9eac5SDavid du Colombier {
85ca9eac5SDavid du Colombier 	NSTEP		= 10,		/* number of steps between throws */
95ca9eac5SDavid du Colombier 	RBALL		= 10,		/* radius of ball images */
105ca9eac5SDavid du Colombier 	Nball		= 100,
115ca9eac5SDavid du Colombier };
125ca9eac5SDavid du Colombier 
135ca9eac5SDavid du Colombier Image *image, **disk;
145ca9eac5SDavid du Colombier int ndisk=0;
158bfd7700SDavid du Colombier int nhand=2;
168bfd7700SDavid du Colombier int delay=20;			/* ms delay between steps */
178bfd7700SDavid du Colombier int nball;
188bfd7700SDavid du Colombier int maxhgt;
198bfd7700SDavid du Colombier Rectangle win;
208bfd7700SDavid du Colombier 
215ca9eac5SDavid du Colombier 
228bfd7700SDavid du Colombier #define add addpt
238bfd7700SDavid du Colombier #define sub subpt
248bfd7700SDavid du Colombier #define inset insetrect
258bfd7700SDavid du Colombier 
265ca9eac5SDavid du Colombier 
278bfd7700SDavid du Colombier /*
288bfd7700SDavid du Colombier  * pattern lists the heights of a repeating sequence of throws.
298bfd7700SDavid du Colombier  * At time t, hand t%nhand throws.  At that time, it must
308bfd7700SDavid du Colombier  * hold exactly one ball, unless it executes a 0 throw,
318bfd7700SDavid du Colombier  * in which case it must hold no ball.  A throw of height h
328bfd7700SDavid du Colombier  * at time t lands at time t+h in hand (t+h)%nhand.
338bfd7700SDavid du Colombier  */
348bfd7700SDavid du Colombier typedef struct Ball Ball;
358bfd7700SDavid du Colombier struct Ball{
368bfd7700SDavid du Colombier 	int oldhand;	/* hand that previously held the ball */
378bfd7700SDavid du Colombier 	int hgt;	/* how high the throw from oldhand was */
388bfd7700SDavid du Colombier 	int time;	/* time at which ball will arrive */
398bfd7700SDavid du Colombier 	int hand;	/* hand in which ball will rest on arrival */
408bfd7700SDavid du Colombier };
415ca9eac5SDavid du Colombier Ball ball[Nball];
throw(int t,int hgt)428bfd7700SDavid du Colombier void throw(int t, int hgt){
438bfd7700SDavid du Colombier 	int hand=t%nhand;
448bfd7700SDavid du Colombier 	int i, b, n;
458bfd7700SDavid du Colombier 	b=n=0;
468bfd7700SDavid du Colombier 	for(i=0;i!=nball;i++) if(ball[i].hand==hand && ball[i].time<=t){
478bfd7700SDavid du Colombier 		n++;
488bfd7700SDavid du Colombier 		b=i;
498bfd7700SDavid du Colombier 	}
508bfd7700SDavid du Colombier 	if(hgt==0){
518bfd7700SDavid du Colombier 		if(n!=0){
528bfd7700SDavid du Colombier 			print("bad zero throw at t=%d, nball=%d\n", t, n);
538bfd7700SDavid du Colombier 			exits("bad");
548bfd7700SDavid du Colombier 		}
558bfd7700SDavid du Colombier 	}
568bfd7700SDavid du Colombier 	else if(n!=1){
578bfd7700SDavid du Colombier 		print("bad ball count at t=%d, nball=%d\n", t, n);
588bfd7700SDavid du Colombier 		exits("bad");
598bfd7700SDavid du Colombier 	}
608bfd7700SDavid du Colombier 	else{
618bfd7700SDavid du Colombier 		ball[b].oldhand=hand;
628bfd7700SDavid du Colombier 		ball[b].hgt=hgt;
638bfd7700SDavid du Colombier 		ball[b].time=t+hgt;
648bfd7700SDavid du Colombier 		ball[b].hand=(hand+hgt)%nhand;
658bfd7700SDavid du Colombier 	}
668bfd7700SDavid du Colombier }
bpos(int b,int step,int t)678bfd7700SDavid du Colombier Point bpos(int b, int step, int t){
688bfd7700SDavid du Colombier 	Ball *bp=&ball[b];
698bfd7700SDavid du Colombier 	double dt=t-1+(step+1.)/NSTEP-(bp->time-bp->hgt);
708bfd7700SDavid du Colombier 	double hgt=(bp->hgt*dt-dt*dt)*4./(maxhgt*maxhgt);
718bfd7700SDavid du Colombier 	double alpha=(bp->oldhand+(bp->hand-bp->oldhand)*dt/bp->hgt)/(nhand-1);
728bfd7700SDavid du Colombier 	return (Point){win.min.x+(win.max.x-win.min.x)*alpha,
738bfd7700SDavid du Colombier 		       win.max.y-1+(win.min.y-win.max.y)*hgt};
748bfd7700SDavid du Colombier }
755ca9eac5SDavid du Colombier 
move(int t)768bfd7700SDavid du Colombier void move(int t){
778bfd7700SDavid du Colombier 	int i, j;
788bfd7700SDavid du Colombier 	for(i=0;i!=NSTEP;i++){
798bfd7700SDavid du Colombier 		if(ecanmouse()) emouse();
808bfd7700SDavid du Colombier 		draw(image, inset(image->r, 3), display->white, nil, ZP);
818bfd7700SDavid du Colombier 		for(j=0;j!=nball;j++)
82*5000fddeSDavid du Colombier 			fillellipse(image, bpos(j, i, t), RBALL, RBALL, disk[j%ndisk], ZP);
838bfd7700SDavid du Colombier 		draw(screen, screen->r, image, nil, image->r.min);
848bfd7700SDavid du Colombier 		flushimage(display, 1);
858bfd7700SDavid du Colombier 		if(delay>0)
868bfd7700SDavid du Colombier 			sleep(delay);
878bfd7700SDavid du Colombier 	}
888bfd7700SDavid du Colombier }
898bfd7700SDavid du Colombier 
908bfd7700SDavid du Colombier void
adddisk(int c)915ca9eac5SDavid du Colombier adddisk(int c)
925ca9eac5SDavid du Colombier {
935ca9eac5SDavid du Colombier 	Image *col;
945ca9eac5SDavid du Colombier 	disk = realloc(disk, (ndisk+1)*sizeof(Image*));
955ca9eac5SDavid du Colombier 	col=allocimage(display, Rect(0,0,1,1), CMAP8, 1, c);
96*5000fddeSDavid du Colombier 	disk[ndisk]=col;
975ca9eac5SDavid du Colombier 	ndisk++;
985ca9eac5SDavid du Colombier }
995ca9eac5SDavid du Colombier 
1005ca9eac5SDavid du Colombier void
diskinit(void)1015ca9eac5SDavid du Colombier diskinit(void)
1025ca9eac5SDavid du Colombier {
1035ca9eac5SDavid du Colombier 	/* colors taken from /sys/src/cmd/stats.c */
1045ca9eac5SDavid du Colombier 
1055ca9eac5SDavid du Colombier 	adddisk(0xFFAAAAFF);
1065ca9eac5SDavid du Colombier 	adddisk(DPalegreygreen);
1075ca9eac5SDavid du Colombier 	adddisk(DDarkyellow);
1085ca9eac5SDavid du Colombier 	adddisk(DMedgreen);
1095ca9eac5SDavid du Colombier 	adddisk(0x00AAFFFF);
1105ca9eac5SDavid du Colombier 	adddisk(0xCCCCCCFF);
1115ca9eac5SDavid du Colombier 
1125ca9eac5SDavid du Colombier 	adddisk(0xBB5D5DFF);
1135ca9eac5SDavid du Colombier 	adddisk(DPurpleblue);
1145ca9eac5SDavid du Colombier 	adddisk(DYellowgreen);
1155ca9eac5SDavid du Colombier 	adddisk(DDarkgreen);
1165ca9eac5SDavid du Colombier 	adddisk(0x0088CCFF);
1175ca9eac5SDavid du Colombier 	adddisk(0x888888FF);
1185ca9eac5SDavid du Colombier }
1195ca9eac5SDavid du Colombier 
1205ca9eac5SDavid du Colombier void
usage(char * name)1218bfd7700SDavid du Colombier usage(char *name)
1228bfd7700SDavid du Colombier {
1238bfd7700SDavid du Colombier 	fprint(2, "usage: %s [start] pattern\n", name);
1248bfd7700SDavid du Colombier 	exits("usage");
1258bfd7700SDavid du Colombier }
1268bfd7700SDavid du Colombier 
1278bfd7700SDavid du Colombier void
eresized(int new)1288bfd7700SDavid du Colombier eresized(int new){
1298bfd7700SDavid du Colombier 	if(new && getwindow(display, Refnone) < 0) {
1308bfd7700SDavid du Colombier 		sysfatal("can't reattach to window");
1318bfd7700SDavid du Colombier 	}
1328bfd7700SDavid du Colombier 	if(image) freeimage(image);
1338bfd7700SDavid du Colombier 	image=allocimage(display, screen->r, screen->chan, 0, DNofill);
1348bfd7700SDavid du Colombier 	draw(image, image->r, display->black, nil, ZP);
1358bfd7700SDavid du Colombier 	win=inset(screen->r, 4+2*RBALL);
1368bfd7700SDavid du Colombier }
1378bfd7700SDavid du Colombier void
main(int argc,char * argv[])1388bfd7700SDavid du Colombier main(int argc, char *argv[]){
1398bfd7700SDavid du Colombier 	int sum, i, t, hgt, nstart, npattern;
1408bfd7700SDavid du Colombier 	char *s, *start = nil, *pattern = nil;
1418bfd7700SDavid du Colombier 
1428bfd7700SDavid du Colombier 	ARGBEGIN{
1438bfd7700SDavid du Colombier 	default:
1448bfd7700SDavid du Colombier 		usage(argv0);
1458bfd7700SDavid du Colombier 	case 'd':
1468bfd7700SDavid du Colombier 		s = ARGF();
1478bfd7700SDavid du Colombier 		if(s == nil)
1488bfd7700SDavid du Colombier 			usage(argv0);
1498bfd7700SDavid du Colombier 		delay = strtol(argv[0], &s, 0);
1508bfd7700SDavid du Colombier 		if(delay < 0 || s == argv[0] || *s != '\0')
1518bfd7700SDavid du Colombier 			usage(argv0);
1528bfd7700SDavid du Colombier 		break;
1538bfd7700SDavid du Colombier 	case 'h':
1548bfd7700SDavid du Colombier 		s = ARGF();
1558bfd7700SDavid du Colombier 		if(s == nil)
1568bfd7700SDavid du Colombier 			usage(argv0);
1578bfd7700SDavid du Colombier 		nhand = strtol(argv[0], &s, 0);
1588bfd7700SDavid du Colombier 		if(nhand <= 0 || s == argv[0] || *s != '\0')
1598bfd7700SDavid du Colombier 			usage(argv0);
1608bfd7700SDavid du Colombier 		break;
1618bfd7700SDavid du Colombier 	}ARGEND
1628bfd7700SDavid du Colombier 
1638bfd7700SDavid du Colombier 	switch(argc) {
1648bfd7700SDavid du Colombier 	case 1:
1658bfd7700SDavid du Colombier 			start="";
1668bfd7700SDavid du Colombier 			pattern=argv[0];
1678bfd7700SDavid du Colombier 			break;
1688bfd7700SDavid du Colombier 	case 2:
1698bfd7700SDavid du Colombier 			start=argv[0];
1708bfd7700SDavid du Colombier 			pattern=argv[1];
1718bfd7700SDavid du Colombier 			break;
1728bfd7700SDavid du Colombier 	default:
1738bfd7700SDavid du Colombier 			usage(argv0);
1748bfd7700SDavid du Colombier 	}
1758bfd7700SDavid du Colombier 	sum=0;
1768bfd7700SDavid du Colombier 	maxhgt=0;
1778bfd7700SDavid du Colombier 	for(s=pattern;*s;s++){
1788bfd7700SDavid du Colombier 		hgt=*s-'0';
1798bfd7700SDavid du Colombier 		sum+=hgt;
1808bfd7700SDavid du Colombier 		if(maxhgt<hgt) maxhgt=hgt;
1818bfd7700SDavid du Colombier 	}
1828bfd7700SDavid du Colombier 	npattern=s-pattern;
1838bfd7700SDavid du Colombier 	for(s=start;*s;s++){
1848bfd7700SDavid du Colombier 		hgt=*s-'0';
1858bfd7700SDavid du Colombier 		if(maxhgt<hgt) maxhgt=hgt;
1868bfd7700SDavid du Colombier 	}
1878bfd7700SDavid du Colombier 	if(sum%npattern){
1888bfd7700SDavid du Colombier 		print("%s: non-integral ball count\n",argv[0]);
1898bfd7700SDavid du Colombier 		exits("partial ball");
1908bfd7700SDavid du Colombier 	}
1918bfd7700SDavid du Colombier 	nball=sum/npattern;
1928bfd7700SDavid du Colombier 	for(i=0;i!=nball;i++){
1938bfd7700SDavid du Colombier 		ball[i].oldhand=(i-nball)%nhand;
1948bfd7700SDavid du Colombier 		if(ball[i].oldhand<0) ball[i].oldhand+=nhand;
1958bfd7700SDavid du Colombier 		ball[i].hgt=nball;
1968bfd7700SDavid du Colombier 		ball[i].time=i;
1978bfd7700SDavid du Colombier 		ball[i].hand=i%nhand;
1988bfd7700SDavid du Colombier 	}
1998bfd7700SDavid du Colombier 	if(initdraw(nil, nil, "juggle") < 0)
2008bfd7700SDavid du Colombier 		sysfatal("initdraw failed: %r");
2018bfd7700SDavid du Colombier 	einit(Emouse);
2025ca9eac5SDavid du Colombier 	diskinit();
2038bfd7700SDavid du Colombier 	eresized(0);
2048bfd7700SDavid du Colombier 	if(image==0){
2058bfd7700SDavid du Colombier 		print("can't allocate bitmap");
2068bfd7700SDavid du Colombier 		exits("no space");
2078bfd7700SDavid du Colombier 	}
2088bfd7700SDavid du Colombier 	for(t=0;start[t];t++){
2098bfd7700SDavid du Colombier 		move(t);
2108bfd7700SDavid du Colombier 		throw(t, start[t]-'0');
2118bfd7700SDavid du Colombier 	}
2128bfd7700SDavid du Colombier 	nstart=t;
2138bfd7700SDavid du Colombier 	for(;;t++){
2148bfd7700SDavid du Colombier 		move(t);
2158bfd7700SDavid du Colombier 		throw(t, pattern[(t-nstart)%npattern]-'0');
2168bfd7700SDavid du Colombier 	}
2178bfd7700SDavid du Colombier }
218