2 * Copyright (c) 2008-2009 Apple Inc. All rights reserved.
4 * @APPLE_DTS_LICENSE_HEADER_START@
6 * IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc.
7 * ("Apple") in consideration of your agreement to the following terms, and your
8 * use, installation, modification or redistribution of this Apple software
9 * constitutes acceptance of these terms. If you do not agree with these terms,
10 * please do not use, install, modify or redistribute this Apple software.
12 * In consideration of your agreement to abide by the following terms, and
13 * subject to these terms, Apple grants you a personal, non-exclusive license,
14 * under Apple's copyrights in this original Apple software (the "Apple Software"),
15 * to use, reproduce, modify and redistribute the Apple Software, with or without
16 * modifications, in source and/or binary forms; provided that if you redistribute
17 * the Apple Software in its entirety and without modifications, you must retain
18 * this notice and the following text and disclaimers in all such redistributions
19 * of the Apple Software. Neither the name, trademarks, service marks or logos of
20 * Apple Computer, Inc. may be used to endorse or promote products derived from
21 * the Apple Software without specific prior written permission from Apple. Except
22 * as expressly stated in this notice, no other rights or licenses, express or
23 * implied, are granted by Apple herein, including but not limited to any patent
24 * rights that may be infringed by your derivative works or by other works in
25 * which the Apple Software may be incorporated.
27 * The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO
28 * WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED
29 * WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
30 * PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN
31 * COMBINATION WITH YOUR PRODUCTS.
33 * IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR
34 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
35 * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
36 * ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR
37 * DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF
38 * CONTRACT, TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF
39 * APPLE HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
41 * @APPLE_DTS_LICENSE_HEADER_END@
45 An asynchronous variation of Conway's Game of Life implemented with
46 GCD. Like the classic version, the game board consists of a grid of
47 cells that can live, die or multiply by the following rules[1]:
49 1. Survivals. Every [living cell] with two or three neighboring
50 [living cells] survives for the next generation.
51 2. Deaths. Each [living cell] wiht four or more neighbors dies (is
52 removed) from overpopulation. Every [living cell] with one
53 neighbor or none dies from isolation.
54 3. Births. Each empty cell adjacent to exactly three neighbors--no
55 more, no fewer--is a birth cell. A [living cell] is placed on it
58 However, unlike the classic version, not all deaths and births occur
59 simultaneously in a single, synchronous, "move" of the game board.
60 Instead the rules are applies to each cell independently based on its
61 observations of the cells around it.
63 Each cell is backed by a GCD queue which manages the synchronization
64 of the cells internal state (living or dead). When a cell's state
65 changes, a notification in the form of a dispatch_call() of the
66 cell_needs_update() work function is sent to all adjacent cells so
67 that the state of those cells may be re-evaluated.
69 To re-evaluate the state of a cell, a request of the current state of
70 all adjecent cells is sent in the form of a dispatch_call() of the
71 _cell_is_alive() work function. The state of the adjacent cells is
72 returned to the requestor via the _cell_is_alive_callback() completion
73 callback. Once all outstanding completion callbacks have been
74 received, the cell updates its state according to the aforementioned
75 rules. If the application of these rules results in another state
76 change, the update_cell() notification is once again sent out,
77 repeating the process.
79 Due to the highly asynchronous nature of this implementation, the
80 simulation's results may differ from the classic version for the same
81 set of initial conditions. In particular, due to non-deterministic
82 scheduling factors, the same set of initial condiitions is likely to
83 produce dramatically different results on subsequent simulations.
85 [1] Martin Gardner. "MATHEMATICAL GAMES: The fantastic combinations of
86 John Conway's new solitaire game 'life'" Scientific American 223
87 (October 1970): 120-123.
89 @copyright Copyright (c) 2008-2009 Apple Inc. All rights reserved.
92 ////////////////////////////////////////////////////////////////////////////////
94 // Adjustable parameters
95 unsigned long grid_x_size
= 40;
96 unsigned long grid_y_size
= 20;
100 ////////////////////////////////////////////////////////////////////////////////
106 #include <sys/ioctl.h>
107 #include <dispatch/dispatch.h>
109 #define CELL_MAX_NEIGHBORS 8
116 // tracks whether a update_cell() notification arrived while
117 // an update was already in progress
119 int living_neighbors
;
120 int queries_outstanding
;
122 struct cell
* neighbors
[CELL_MAX_NEIGHBORS
];
124 } __attribute__((aligned(64)));
126 ////////////////////////////////////////////////////////////////////////////////
128 /*! @function init_grid
129 Initializes the grid data structure based on the global variables
130 grid_x_size and grid_y_size. Must be called before any calls to
132 struct cell
* init_grid(size_t grid_x_size
, size_t grid_y_size
);
134 /*! @function init_display
135 Initializes the display subsystem. Starts a periodic timer to update the
136 display based on the current contents of the cell grid.
138 void init_display(struct cell
* grid
);
140 ////////////////////////////////////////////////////////////////////////////////
142 // Macro to test whether x,y coordinates are within bounds of the grid
143 #define GRID_VALID(u,v) (((u) >= 0) && ((v) >= 0) && \
144 ((u) < grid_x_size) && ((v) < grid_y_size))
145 // Macro to translate from 2d grid coordinates to array offest
146 #define GRID_OFF(u,v) ((v) * grid_x_size + (u))
148 #if !defined(DISPATCH_LIFE_GL)
149 int main(int argc
, char* argv
[]) {
154 res
= ioctl(STDIN_FILENO
, TIOCGWINSZ
, &tsz
);
156 grid_x_size
= tsz
.ts_cols
;
157 grid_y_size
= tsz
.ts_lines
;
163 while ((ch
= getopt(argc
, argv
, "x:y:q")) != -1) {
167 grid_x_size
= strtol(optarg
, &endptr
, 10);
168 if (grid_x_size
< 0 || (endptr
&& *endptr
!= 0)) {
169 fprintf(stderr
, "life: invalid x size\n");
174 grid_y_size
= strtol(optarg
, &endptr
, 10);
175 if (grid_y_size
< 0 || (endptr
&& *endptr
!= 0)) {
176 fprintf(stderr
, "life: invalid y size\n");
185 fprintf(stderr
, "usage: life [-q] [-x size] [-y size]\n");
186 fprintf(stderr
, "\t-x: grid x size (default is terminal columns)\n");
187 fprintf(stderr
, "\t-y: grid y size (default is terminal rows)\n");
188 fprintf(stderr
, "\t-q: suppress display output\n");
193 struct cell
* grid
= init_grid(grid_x_size
, grid_y_size
);
198 initscr(); cbreak(); noecho();
200 intrflush(stdscr
, FALSE
);
201 keypad(stdscr
, TRUE
);
207 if (dispflag
&& use_curses
) {
213 #endif /* defined(DISPATCH_LIFE_GL) */
215 ////////////////////////////////////////////////////////////////////////////////
217 static void cell_set_alive(struct cell
*, int alive
);
219 /*! @function update_cell
220 GCD work function. Begins the update process for a cell by
221 sending cell_is_alive() messages with cell_is_alive_callback()
222 completion callbacks to all adjacent cells. If an update is already
223 in progress, simply sets the needs_update flag of the cell. */
224 static void update_cell(struct cell
*);
226 /*! @function cell_is_alive_callback
227 GCD completion callback. Receives the result from cell_is_alive. When
228 all _cell_is_alive_callback() completion callbacks have been received
229 from an update, recalculates the internal state of the cell. If the
230 state changes, sends update_cell() to all adjacent cells. */
231 static void update_cell_response(struct cell
*, int);
233 ////////////////////////////////////////////////////////////////////////////////
236 foreach_neighbor(struct cell
* self
, void (^action
)(struct cell
* other
)) {
238 for (i
= 0; i
< CELL_MAX_NEIGHBORS
; ++i
) {
239 struct cell
* other
= self
->neighbors
[i
];
247 // Change cell state, update the screen, and update neighbors.
249 cell_set_alive(struct cell
* self
, int alive
) {
250 if (alive
== self
->alive
) return; // nothing to do
252 dispatch_async(self
->q
, ^{
254 self
->display
= (self
->alive
) ? '#' : ' ';
256 foreach_neighbor(self
, ^(struct cell
* other
) {
257 dispatch_async(other
->q
, ^{ update_cell(other
); });
263 update_cell(struct cell
* self
) {
264 if (self
->queries_outstanding
== 0) {
265 self
->needs_update
= 0;
266 self
->living_neighbors
= 0;
268 foreach_neighbor(self
, ^(struct cell
* other
) {
269 ++self
->queries_outstanding
;
270 dispatch_async(other
->q
, ^{
271 dispatch_async(self
->q
, ^{ update_cell_response(self
, other
->alive
); });
275 // '.' indicates the cell is not alive but needs an update
276 if (!self
->alive
) self
->display
= '.';
278 self
->needs_update
= 1;
283 update_cell_response(struct cell
* self
, int response
) {
284 if (response
) ++self
->living_neighbors
;
285 --self
->queries_outstanding
;
287 // when all neighbors have replied with their state,
288 // recalculate our internal state
289 if (self
->queries_outstanding
== 0) {
290 const int living_neighbors
= self
->living_neighbors
;
291 int alive
= self
->alive
;
293 // Conway's Game of Life
294 if (living_neighbors
< 2 || living_neighbors
> 3) {
296 } else if (living_neighbors
== 3) {
300 // Notify neighbors of state change
301 cell_set_alive(self
, alive
);
303 // if a request for an update came in while we were
304 // already processing one, kick off the next update
305 if (self
->needs_update
) {
306 dispatch_async(self
->q
, ^{ update_cell(self
); });
308 // otherwise clear the '.' character that was
309 // displayed during the update
317 ////////////////////////////////////////////////////////////////////////////////
320 init_grid(size_t grid_x_size
, size_t grid_y_size
) {
321 struct cell
* grid
= calloc(sizeof(struct cell
),grid_x_size
*grid_y_size
);
324 for (i
= 0; i
< grid_x_size
; ++i
) {
325 for (j
= 0; j
< grid_y_size
; ++j
) {
326 struct cell
* ptr
= &grid
[GRID_OFF(i
,j
)];
328 asprintf(&ptr
->label
, "x%dy%d", i
, j
);
330 ptr
->q
= dispatch_queue_create(ptr
->label
, NULL
);
331 dispatch_set_target_queue(ptr
->q
, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW
, 0));
332 dispatch_queue_set_context(ptr
->q
, ptr
);
334 ptr
->neighbors
[0] = GRID_VALID(i
,j
-1) ?
335 &grid
[GRID_OFF(i
,j
-1)] : NULL
; // N
336 ptr
->neighbors
[1] = GRID_VALID(i
+1,j
-1) ?
337 &grid
[GRID_OFF(i
+1,j
-1)] : NULL
; // NE
338 ptr
->neighbors
[2] = GRID_VALID(i
+1,j
) ?
339 &grid
[GRID_OFF(i
+1,j
)] : NULL
; // E
340 ptr
->neighbors
[3] = GRID_VALID(i
+1,j
+1) ?
341 &grid
[GRID_OFF(i
+1,j
+1)] : NULL
; // SE
342 ptr
->neighbors
[4] = GRID_VALID(i
,j
+1) ?
343 &grid
[GRID_OFF(i
,j
+1)] : NULL
; // S
344 ptr
->neighbors
[5] = GRID_VALID(i
-1,j
+1) ?
345 &grid
[GRID_OFF(i
-1,j
+1)] : NULL
; // SW
346 ptr
->neighbors
[6] = GRID_VALID(i
-1,j
) ?
347 &grid
[GRID_OFF(i
-1,j
)] : NULL
; // W
348 ptr
->neighbors
[7] = GRID_VALID(i
-1,j
-1) ?
349 &grid
[GRID_OFF(i
-1,j
-1)] : NULL
; // NW
354 for (i
= 0; i
< grid_x_size
; ++i
) {
355 for (j
= 0; j
< grid_y_size
; ++j
) {
357 cell_set_alive(&grid
[GRID_OFF(i
,j
)], 1);
365 #if defined(DISPATCH_LIFE_GL)
367 get_grid_display_char(struct cell
* grid
, size_t x
, size_t y
) {
368 return grid
[GRID_OFF(x
,y
)].display
;
370 #endif /* defined(DISPATCH_LIFE_GL) */
372 #if !defined(DISPATCH_LIFE_GL)
374 init_display(struct cell
* grid
)
376 dispatch_source_t timer
;
378 timer
= dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER
, 0, 0, dispatch_get_main_queue());
379 dispatch_source_set_timer(dispatch_time(DISPATCH_TIME_NOW
, 0). 10000000, 1000);
380 dispatch_source_set_event_handler(^{
383 for (x
= 0; x
< grid_x_size
; ++x
) {
384 for (y
= 0; y
< grid_y_size
; ++y
) {
385 mvaddnstr(y
, x
, &grid
[GRID_OFF(x
,y
)].display
, 1);
390 dispatch_resume(timer
);
392 #endif /* defined(DISPATCH_LIFE_GL) */