From 647531dced5cb79107b768418cbf400b583300c2 Mon Sep 17 00:00:00 2001 From: Sam Anthony Date: Wed, 30 Oct 2024 19:38:10 -0400 Subject: ring broadcast --- balls.c | 162 +++++++++++++++++++++++----------------------------------------- 1 file changed, 58 insertions(+), 104 deletions(-) diff --git a/balls.c b/balls.c index f156aab..adf7964 100644 --- a/balls.c +++ b/balls.c @@ -39,20 +39,19 @@ typedef struct { Channel *frametick; - Channel **in; /* <-chan Ball */ - Channel **out; /* chan<- Ball */ + /* threads communicate in a ring */ + Channel *lneighbor; /* <-chan Ball */ + Channel *rneighbor; /* chan<- Ball */ int nothers; /* number of other balls */ } BallArg; static int ballcolors[] = { DRed, DGreen, DBlue }; -static uint radii[] = { 20, 30, 50 }; +static uint radii[] = { 24, 32, 48 }; static Rectangle bounds = {{0, 0}, {WIDTH, HEIGHT}}; -int init(char *label, Mousectl **mctl, Keyboardctl **kctl); void spawnballs(int n); +void eventloop(Image *bg, Image *walls); Channel **allocchans(int nchans, int elsize, int nel); -void mcopycolskip(Channel *vec[], Channel **matrix[], int n, int col, int skip); -void vcopyskip(Channel *dst[], Channel *src[], int n, int skip); void nooverlapcircles(Point centers[], int n, uint radius); Point randptinrect(Rectangle r); int randint(int lo, int hi); @@ -60,17 +59,12 @@ uint maxelem(uint arr[], uint n); double mass(uint radius); double area(double radius); void ball(void *arg); -void broadcast(Channel *cs[], int n, void *v); void frametick(void *arg); void threadmain(int argc, char *argv[]) { int nballs; Image *bg, *walls; - Mousectl *mctl; - Keyboardctl *kctl; - int resize[2]; - Rune key; nballs = DEFAULT_NBALLS; if (argc > 1) { @@ -81,8 +75,8 @@ threadmain(int argc, char *argv[]) { } printf("nballs: %ud\n", nballs); - if (init(argv[0], &mctl, &kctl)) - sysfatal("%s: %r", argv[0]); + if (initdraw(nil, nil, "Balls") < 0) + sysfatal("initdraw() failed\n"); printf("screen: (%d,%d) (%d,%d)\n", screen->r.min.x, screen->r.min.y, screen->r.max.x, screen->r.max.y); @@ -98,47 +92,16 @@ threadmain(int argc, char *argv[]) { spawnballs(nballs); - enum { RESIZE = 0, KEYBD = 1 }; - Alt alts[3] = { - {mctl->resizec, &resize, CHANRCV}, - {kctl->c, &key, CHANRCV}, - {nil, nil, CHANEND}, - }; - for (;;) { - switch (alt(alts)) { - case RESIZE: - if (getwindow(display, Refnone) < 0) - sysfatal("%s: %r", argv[0]); - drawbg(walls, bg); - break; - case KEYBD: - if (key == KEY_QUIT) - threadexitsall(0); - break; - case -1: /* interrupted */ - break; - } - } -} - -int -init(char *label, Mousectl **mctl, Keyboardctl **kctl) { - if (initdraw(nil, nil, label) < 0) - return 1; - if ((*mctl = initmouse(nil, screen)) == nil) - return 1; - if ((*kctl = initkeyboard(nil)) == nil) - return 1; - return 0; + eventloop(bg, walls); } void spawnballs(int n) { Channel **ticks; Point *ps; - int i, j; + int i; BallArg *arg; - Channel ***cs; + Channel **cs; if ((ticks = allocchans(n, sizeof(int), TICK_BUFSIZE)) == nil) sysfatal("failed to allocate frame ticker channels"); @@ -149,17 +112,11 @@ spawnballs(int n) { sysfatal("failed to allocate position array"); nooverlapcircles(ps, n, maxelem(radii, NELEMS(radii))); - if ((cs = malloc(n*sizeof(Channel **))) == nil) + if ((cs = malloc(n*sizeof(Channel *))) == nil) sysfatal("failed to allocate channel matrix"); for (i = 0; i < n; i++) { - if ((cs[i] = malloc(n*sizeof(Channel *))) == nil) - sysfatal("failed to allocate row of channel matrix"); - for (j = 0; j < n; j++) { - if (j == i) - continue; - if ((cs[i][j] = chancreate(sizeof(Ball), BALL_BUFSIZE)) == nil) - sysfatal("failed to create channel"); - } + if ((cs[i] = chancreate(sizeof(Ball), BALL_BUFSIZE)) == nil) + sysfatal("failed to create channel"); } for (i = 0; i < n; i++) { @@ -170,19 +127,10 @@ spawnballs(int n) { arg->b.v = V((double) randint(-VMAX, VMAX+1), (double) randint(-VMAX, VMAX+1)); arg->b.r = radii[randint(0, NELEMS(radii))]; arg->b.m = mass(arg->b.r); - arg->color = ballcolors[randint(0, NELEMS(ballcolors))]; - arg->frametick = ticks[i]; - - if ((arg->in = malloc((n-1)*sizeof(Channel *))) == nil) - sysfatal("failed to allocate array of incoming channels"); - mcopycolskip(arg->in, cs, n, i, i); - - if ((arg->out = malloc((n-1)*sizeof(Channel *))) == nil) - sysfatal("failed to allocate array of outgoing channels"); - vcopyskip(arg->out, cs[i], n, i); - + arg->lneighbor = cs[i]; + arg->rneighbor = cs[(i+1) % n]; arg->nothers = n-1; printf("create ball p(%d,%d) v(%f,%f) r(%u) m(%f)\n", arg->b.p.x, arg->b.p.y, arg->b.v.x, arg->b.v.y, arg->b.r, arg->b.m); @@ -190,11 +138,45 @@ spawnballs(int n) { } free(ps); - for (i = 0; i < n; i++) - free(cs[i]); free(cs); } +void +eventloop(Image *bg, Image *walls) { + Mousectl *mctl; + Keyboardctl *kctl; + int resize[2]; + Rune key; + + if ((mctl = initmouse(nil, screen)) == nil) + sysfatal("failed to initialize mouse\n"); + if ((kctl = initkeyboard(nil)) == nil) + sysfatal("failed to initialize keyboard\n"); + + enum { RESIZE = 0, KEYBD = 1 }; + Alt alts[3] = { + {mctl->resizec, &resize, CHANRCV}, + {kctl->c, &key, CHANRCV}, + {nil, nil, CHANEND}, + }; + for (;;) { + switch (alt(alts)) { + case RESIZE: + if (getwindow(display, Refnone) < 0) + sysfatal("getwindow() failed\n"); + drawbg(walls, bg); + break; + case KEYBD: + if (key == KEY_QUIT) + threadexitsall(0); + break; + case -1: /* interrupted */ + break; + } + } +} + +/* allocate nil-terminated array of channels */ Channel ** allocchans(int nchans, int elsize, int nel) { Channel **cs; @@ -217,28 +199,7 @@ allocchans(int nchans, int elsize, int nel) { return cs; } -/* copy column col of nxn matrix, except for element skip, into n-1 length vec */ -void -mcopycolskip(Channel *vec[], Channel **matrix[], int n, int col, int skip) { - int i; - - for (i = 0; i < skip; i++) - vec[i] = matrix[i][col]; - for (i = skip+1; i < n; i++) - vec[i-1] = matrix[i][col]; -} - -/* copy each element in n-length src, except for element skip, into n-1 length dst */ -void -vcopyskip(Channel *dst[], Channel *src[], int n, int skip) { - int i; - - for (i = 0; i < skip; i++) - dst[i] = src[i]; - for (i = skip+1; i < n; i++) - dst[i-1] = src[i]; -} - +/* populate array of circle coordinates within bounds so that circles don't overlap */ void nooverlapcircles(Point centers[], int n, uint radius) { int i, j; @@ -305,38 +266,31 @@ ball(void *arg) { if (fill == nil ||erase == nil) sysfatal("failed to allocate image"); - oldpos = b.p; + oldpos = b.p; /* keep track of previous position so it can be erased */ for (;;) { b.v.y += G; - b.p = ptaddv(b.p, b.v); printf("(%d,%d) %f %f\n", b.p.x, b.p.y, b.v.x, b.v.y); - broadcast(barg->out, barg->nothers, &b); - /* check for ball collision */ + other = b; /* send our own ball first */ for (i = 0; i < barg->nothers; i++) { - recv(barg->in[i], &other); + send(barg->rneighbor, &other); /* pass balls around the ring */ + recv(barg->lneighbor, &other); collideball(&b, &other); } collidewall(&b, bounds); - recv(barg->frametick, &t); + recv(barg->frametick, &t); /* wait for next frame */ drawcircle(erase, oldpos); drawcircle(fill, b.p); oldpos = b.p; - } } -void -broadcast(Channel *cs[], int n, void *v) { - while (n-- > 0) - send(cs[n], v); -} - +/* arg is nil-terminated array of chan int */ void frametick(void *arg) { Channel **cs, **c; -- cgit v1.2.3