xref: /plan9-contrib/sys/src/games/juggle.c (revision 5000fddee1108b517ce6ed55f4cb5000080fb70b)
1 #include <u.h>
2 #include <libc.h>
3 #include <draw.h>
4 #include <event.h>
5 
6 enum
7 {
8 	NSTEP		= 10,		/* number of steps between throws */
9 	RBALL		= 10,		/* radius of ball images */
10 	Nball		= 100,
11 };
12 
13 Image *image, **disk;
14 int ndisk=0;
15 int nhand=2;
16 int delay=20;			/* ms delay between steps */
17 int nball;
18 int maxhgt;
19 Rectangle win;
20 
21 
22 #define add addpt
23 #define sub subpt
24 #define inset insetrect
25 
26 
27 /*
28  * pattern lists the heights of a repeating sequence of throws.
29  * At time t, hand t%nhand throws.  At that time, it must
30  * hold exactly one ball, unless it executes a 0 throw,
31  * in which case it must hold no ball.  A throw of height h
32  * at time t lands at time t+h in hand (t+h)%nhand.
33  */
34 typedef struct Ball Ball;
35 struct Ball{
36 	int oldhand;	/* hand that previously held the ball */
37 	int hgt;	/* how high the throw from oldhand was */
38 	int time;	/* time at which ball will arrive */
39 	int hand;	/* hand in which ball will rest on arrival */
40 };
41 Ball ball[Nball];
throw(int t,int hgt)42 void throw(int t, int hgt){
43 	int hand=t%nhand;
44 	int i, b, n;
45 	b=n=0;
46 	for(i=0;i!=nball;i++) if(ball[i].hand==hand && ball[i].time<=t){
47 		n++;
48 		b=i;
49 	}
50 	if(hgt==0){
51 		if(n!=0){
52 			print("bad zero throw at t=%d, nball=%d\n", t, n);
53 			exits("bad");
54 		}
55 	}
56 	else if(n!=1){
57 		print("bad ball count at t=%d, nball=%d\n", t, n);
58 		exits("bad");
59 	}
60 	else{
61 		ball[b].oldhand=hand;
62 		ball[b].hgt=hgt;
63 		ball[b].time=t+hgt;
64 		ball[b].hand=(hand+hgt)%nhand;
65 	}
66 }
bpos(int b,int step,int t)67 Point bpos(int b, int step, int t){
68 	Ball *bp=&ball[b];
69 	double dt=t-1+(step+1.)/NSTEP-(bp->time-bp->hgt);
70 	double hgt=(bp->hgt*dt-dt*dt)*4./(maxhgt*maxhgt);
71 	double alpha=(bp->oldhand+(bp->hand-bp->oldhand)*dt/bp->hgt)/(nhand-1);
72 	return (Point){win.min.x+(win.max.x-win.min.x)*alpha,
73 		       win.max.y-1+(win.min.y-win.max.y)*hgt};
74 }
75 
move(int t)76 void move(int t){
77 	int i, j;
78 	for(i=0;i!=NSTEP;i++){
79 		if(ecanmouse()) emouse();
80 		draw(image, inset(image->r, 3), display->white, nil, ZP);
81 		for(j=0;j!=nball;j++)
82 			fillellipse(image, bpos(j, i, t), RBALL, RBALL, disk[j%ndisk], ZP);
83 		draw(screen, screen->r, image, nil, image->r.min);
84 		flushimage(display, 1);
85 		if(delay>0)
86 			sleep(delay);
87 	}
88 }
89 
90 void
adddisk(int c)91 adddisk(int c)
92 {
93 	Image *col;
94 	disk = realloc(disk, (ndisk+1)*sizeof(Image*));
95 	col=allocimage(display, Rect(0,0,1,1), CMAP8, 1, c);
96 	disk[ndisk]=col;
97 	ndisk++;
98 }
99 
100 void
diskinit(void)101 diskinit(void)
102 {
103 	/* colors taken from /sys/src/cmd/stats.c */
104 
105 	adddisk(0xFFAAAAFF);
106 	adddisk(DPalegreygreen);
107 	adddisk(DDarkyellow);
108 	adddisk(DMedgreen);
109 	adddisk(0x00AAFFFF);
110 	adddisk(0xCCCCCCFF);
111 
112 	adddisk(0xBB5D5DFF);
113 	adddisk(DPurpleblue);
114 	adddisk(DYellowgreen);
115 	adddisk(DDarkgreen);
116 	adddisk(0x0088CCFF);
117 	adddisk(0x888888FF);
118 }
119 
120 void
usage(char * name)121 usage(char *name)
122 {
123 	fprint(2, "usage: %s [start] pattern\n", name);
124 	exits("usage");
125 }
126 
127 void
eresized(int new)128 eresized(int new){
129 	if(new && getwindow(display, Refnone) < 0) {
130 		sysfatal("can't reattach to window");
131 	}
132 	if(image) freeimage(image);
133 	image=allocimage(display, screen->r, screen->chan, 0, DNofill);
134 	draw(image, image->r, display->black, nil, ZP);
135 	win=inset(screen->r, 4+2*RBALL);
136 }
137 void
main(int argc,char * argv[])138 main(int argc, char *argv[]){
139 	int sum, i, t, hgt, nstart, npattern;
140 	char *s, *start = nil, *pattern = nil;
141 
142 	ARGBEGIN{
143 	default:
144 		usage(argv0);
145 	case 'd':
146 		s = ARGF();
147 		if(s == nil)
148 			usage(argv0);
149 		delay = strtol(argv[0], &s, 0);
150 		if(delay < 0 || s == argv[0] || *s != '\0')
151 			usage(argv0);
152 		break;
153 	case 'h':
154 		s = ARGF();
155 		if(s == nil)
156 			usage(argv0);
157 		nhand = strtol(argv[0], &s, 0);
158 		if(nhand <= 0 || s == argv[0] || *s != '\0')
159 			usage(argv0);
160 		break;
161 	}ARGEND
162 
163 	switch(argc) {
164 	case 1:
165 			start="";
166 			pattern=argv[0];
167 			break;
168 	case 2:
169 			start=argv[0];
170 			pattern=argv[1];
171 			break;
172 	default:
173 			usage(argv0);
174 	}
175 	sum=0;
176 	maxhgt=0;
177 	for(s=pattern;*s;s++){
178 		hgt=*s-'0';
179 		sum+=hgt;
180 		if(maxhgt<hgt) maxhgt=hgt;
181 	}
182 	npattern=s-pattern;
183 	for(s=start;*s;s++){
184 		hgt=*s-'0';
185 		if(maxhgt<hgt) maxhgt=hgt;
186 	}
187 	if(sum%npattern){
188 		print("%s: non-integral ball count\n",argv[0]);
189 		exits("partial ball");
190 	}
191 	nball=sum/npattern;
192 	for(i=0;i!=nball;i++){
193 		ball[i].oldhand=(i-nball)%nhand;
194 		if(ball[i].oldhand<0) ball[i].oldhand+=nhand;
195 		ball[i].hgt=nball;
196 		ball[i].time=i;
197 		ball[i].hand=i%nhand;
198 	}
199 	if(initdraw(nil, nil, "juggle") < 0)
200 		sysfatal("initdraw failed: %r");
201 	einit(Emouse);
202 	diskinit();
203 	eresized(0);
204 	if(image==0){
205 		print("can't allocate bitmap");
206 		exits("no space");
207 	}
208 	for(t=0;start[t];t++){
209 		move(t);
210 		throw(t, start[t]-'0');
211 	}
212 	nstart=t;
213 	for(;;t++){
214 		move(t);
215 		throw(t, pattern[(t-nstart)%npattern]-'0');
216 	}
217 }
218