#include #include #include #include enum { Ncolor = 6, NSTEP = 10, /* number of steps between throws */ RBALL = 10, /* radius of ball images */ Nball = 100, }; int nhand=2; int delay=20; /* ms delay between steps */ int nball; int maxhgt; Rectangle win; #define add addpt #define sub subpt #define inset insetrect /* * pattern lists the heights of a repeating sequence of throws. * At time t, hand t%nhand throws. At that time, it must * hold exactly one ball, unless it executes a 0 throw, * in which case it must hold no ball. A throw of height h * at time t lands at time t+h in hand (t+h)%nhand. */ typedef struct Ball Ball; struct Ball{ int oldhand; /* hand that previously held the ball */ int hgt; /* how high the throw from oldhand was */ int time; /* time at which ball will arrive */ int hand; /* hand in which ball will rest on arrival */ }; Ball ball[Nball]; void throw(int t, int hgt){ int hand=t%nhand; int i, b, n; b=n=0; for(i=0;i!=nball;i++) if(ball[i].hand==hand && ball[i].time<=t){ n++; b=i; } if(hgt==0){ if(n!=0){ print("bad zero throw at t=%d, nball=%d\n", t, n); exits("bad"); } } else if(n!=1){ print("bad ball count at t=%d, nball=%d\n", t, n); exits("bad"); } else{ ball[b].oldhand=hand; ball[b].hgt=hgt; ball[b].time=t+hgt; ball[b].hand=(hand+hgt)%nhand; } } Point bpos(int b, int step, int t){ Ball *bp=&ball[b]; double dt=t-1+(step+1.)/NSTEP-(bp->time-bp->hgt); double hgt=(bp->hgt*dt-dt*dt)*4./(maxhgt*maxhgt); double alpha=(bp->oldhand+(bp->hand-bp->oldhand)*dt/bp->hgt)/(nhand-1); return (Point){win.min.x+(win.max.x-win.min.x)*alpha, win.max.y-1+(win.min.y-win.max.y)*hgt}; } Image *image, *cols[Ncolor], *disk[Ncolor]; void move(int t){ int i, j; for(i=0;i!=NSTEP;i++){ if(ecanmouse()) emouse(); draw(image, inset(image->r, 3), display->white, nil, ZP); for(j=0;j!=nball;j++) draw(image, rectaddpt(disk[j%Ncolor]->r, sub(bpos(j, i, t), Pt(RBALL, RBALL))), disk[j%Ncolor], nil, ZP); draw(screen, screen->r, image, nil, image->r.min); flushimage(display, 1); if(delay>0) sleep(delay); } } void mkdisk(int i, int c) { cols[i]=allocimage(display, Rect(0,0,1,1), CMAP8, 1, c); disk[i]=allocimage(display, Rect(0, 0, 2*RBALL+1, 2*RBALL+1), screen->chan, 0, DWhite); fillellipse(disk[i], Pt(RBALL, RBALL), RBALL, RBALL, cols[i], ZP); } void diskinit(void) { /* colors taken from /sys/src/cmd/stats.c */ mkdisk(0, 0xFFAAAAFF); mkdisk(1, DPalegreygreen); mkdisk(2, DDarkyellow); mkdisk(3, DMedgreen); mkdisk(4, 0x00AAFFFF); mkdisk(5, 0xCCCCCCFF); } void usage(char *name) { fprint(2, "usage: %s [start] pattern\n", name); exits("usage"); } void eresized(int new){ if(new && getwindow(display, Refnone) < 0) { sysfatal("can't reattach to window"); } if(image) freeimage(image); image=allocimage(display, screen->r, screen->chan, 0, DNofill); draw(image, image->r, display->black, nil, ZP); win=inset(screen->r, 4+2*RBALL); } void main(int argc, char *argv[]){ int sum, i, t, hgt, nstart, npattern; char *s, *start = nil, *pattern = nil; ARGBEGIN{ default: usage(argv0); case 'd': s = ARGF(); if(s == nil) usage(argv0); delay = strtol(argv[0], &s, 0); if(delay < 0 || s == argv[0] || *s != '\0') usage(argv0); break; case 'h': s = ARGF(); if(s == nil) usage(argv0); nhand = strtol(argv[0], &s, 0); if(nhand <= 0 || s == argv[0] || *s != '\0') usage(argv0); break; }ARGEND switch(argc) { case 1: start=""; pattern=argv[0]; break; case 2: start=argv[0]; pattern=argv[1]; break; default: usage(argv0); } sum=0; maxhgt=0; for(s=pattern;*s;s++){ hgt=*s-'0'; sum+=hgt; if(maxhgt