]>
git.saurik.com Git - wxWidgets.git/blob - demos/life/game.cpp
07a2f13401969652d64ffe60d750e5603d3d0e8f
1 /////////////////////////////////////////////////////////////////////////////
3 // Purpose: Life! game logic
4 // Author: Guillermo Rodriguez Garcia, <guille@iies.es>
8 // Copyright: (c) 2000, Guillermo Rodriguez Garcia
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
12 // ==========================================================================
13 // headers, declarations, constants
14 // ==========================================================================
17 #pragma implementation "game.h"
23 #include <stdlib.h> // for abort
24 #include <string.h> // for memset
27 #define ARRAYSIZE 1024 // size of the static arrays for BeginFind & co.
28 #define ALLOCBOXES 16 // number of cellboxes to alloc at once
30 // ==========================================================================
32 // ==========================================================================
34 #define HASH(x, y) (((x >> 3) & 0x7f) << 7) + ((y >> 3) & 0x7f)
35 #define HASHSIZE 32768
43 inline bool IsAlive(int dx
, int dy
) const;
44 inline bool SetCell(int dx
, int dy
, bool alive
);
47 wxInt32 m_x
, m_y
; // position in universe
48 wxUint32 m_live1
, m_live2
; // alive cells (1 bit per cell)
49 wxUint32 m_old1
, m_old2
; // old values for m_live1, 2
50 wxUint32 m_on
[8]; // neighbouring info
51 wxUint32 m_dead
; // been dead for n generations
52 CellBox
*m_up
, *m_dn
, *m_lf
, *m_rt
; // neighbour CellBoxes
53 CellBox
*m_prev
, *m_next
; // in linked list
54 CellBox
*m_hprev
, *m_hnext
; // in hash table
59 // Returns whether cell dx, dy in this box is alive
61 bool CellBox::IsAlive(int dx
, int dy
) const
64 return (m_live2
& 1 << ((dy
- 4) * 8 + dx
));
66 return (m_live1
& 1 << ((dy
) * 8 + dx
));
70 // Sets cell dx, dy in this box to 'alive', returns TRUE if
71 // the previous value was different, FALSE if it was the same.
73 bool CellBox::SetCell(int dx
, int dy
, bool alive
)
75 if (IsAlive(dx
, dy
) != alive
)
78 m_live2
^= 1 << ((dy
- 4) * 8 + dx
);
80 m_live1
^= 1 << ((dy
) * 8 + dx
);
82 // reset this here to avoid updating problems
92 // ==========================================================================
94 // ==========================================================================
96 // --------------------------------------------------------------------------
98 // --------------------------------------------------------------------------
103 m_boxes
= new CellBox
*[HASHSIZE
];
106 for (int i
= 0; i
< HASHSIZE
; i
++)
109 m_cells
= new Cell
[ARRAYSIZE
];
124 // Clears the board, freeing all storage.
132 // clear the hash table pointers
133 for (int i
= 0; i
< HASHSIZE
; i
++)
146 // free available boxes
157 // --------------------------------------------------------------------------
158 // Test and set individual cells
159 // --------------------------------------------------------------------------
162 // Returns whether cell (x, y) is alive.
164 bool Life::IsAlive(wxInt32 x
, wxInt32 y
)
166 CellBox
*c
= LinkBox(x
, y
, FALSE
);
168 return (c
&& c
->IsAlive( x
- c
->m_x
, y
- c
->m_y
));
172 // Sets or clears cell (x, y), according to the 'alive' param.
174 void Life::SetCell(wxInt32 x
, wxInt32 y
, bool alive
)
176 CellBox
*c
= LinkBox(x
, y
);
177 wxUint32 dx
= x
- c
->m_x
;
178 wxUint32 dy
= y
- c
->m_y
;
180 if (c
->SetCell(dx
, dy
, alive
))
189 void Life::SetShape(const LifeShape
& shape
)
191 char *p
= shape
.m_data
;
193 int i0
= -(shape
.m_width
/ 2);
194 int j0
= -(shape
.m_height
/ 2);
195 int i1
= i0
+ shape
.m_width
- 1;
196 int j1
= j0
+ shape
.m_height
- 1;
199 for (int j
= j0
; j
<= j1
; j
++)
200 for (int i
= i0
; i
<= i1
; i
++)
201 SetCell(i
, j
, *(p
++) == '*');
204 // --------------------------------------------------------------------------
205 // Cellbox management functions
206 // --------------------------------------------------------------------------
209 // Creates a new box in x, y, either taking it from the list
210 // of available boxes, or allocating a new one.
212 CellBox
* Life::CreateBox(wxInt32 x
, wxInt32 y
, wxUint32 hv
)
216 // if there are no available boxes, alloc a few more
218 for (int i
= 1; i
<= ALLOCBOXES
; i
++)
224 // TODO: handle memory errors. Note that right now, if we
225 // couldn't allocate at least one cellbox, we will crash
226 // before leaving CreateBox(). Probably we should try to
227 // allocate some boxes *before* the m_available list goes
228 // empty, so that we have a margin to handle errors
230 wxLogFatalError(_("Out of memory! Aborting..."));
232 // the above call should abort, but it doesn't :-?
238 c
->m_next
= m_available
;
242 // take a cellbox from the list of available boxes
244 m_available
= c
->m_next
;
247 memset((void *) c
, 0, sizeof(CellBox
));
251 // insert c in the list
254 if (c
->m_next
) c
->m_next
->m_prev
= c
;
256 // insert c in the hash table
257 c
->m_hnext
= m_boxes
[hv
];
259 if (c
->m_hnext
) c
->m_hnext
->m_hprev
= c
;
265 // Returns a pointer to the box (x, y); if it didn't exist yet,
266 // it returns NULL or creates a new one, depending on the 'create'
269 CellBox
* Life::LinkBox(wxInt32 x
, wxInt32 y
, bool create
)
278 // search in the hash table
279 for (c
= m_boxes
[hv
]; c
; c
= c
->m_hnext
)
280 if ((c
->m_x
== x
) && (c
->m_y
== y
)) return c
;
282 // if not found, and (create == TRUE), create a new one
283 return create
? CreateBox(x
, y
, hv
) : NULL
;
287 // Removes this box from the list and the hash table and
288 // puts it in the list of available boxes.
290 void Life::KillBox(CellBox
*c
)
292 wxUint32 hv
= HASH(c
->m_x
, c
->m_y
);
294 // remove from the list
296 c
->m_prev
->m_next
= c
->m_next
;
300 // remove from the hash table
301 if (c
!= m_boxes
[hv
])
302 c
->m_hprev
->m_hnext
= c
->m_hnext
;
304 m_boxes
[hv
] = c
->m_hnext
;
307 if (c
->m_next
) c
->m_next
->m_prev
= c
->m_prev
;
308 if (c
->m_hnext
) c
->m_hnext
->m_hprev
= c
->m_hprev
;
309 if (c
->m_up
) c
->m_up
->m_dn
= NULL
;
310 if (c
->m_dn
) c
->m_dn
->m_up
= NULL
;
311 if (c
->m_lf
) c
->m_lf
->m_rt
= NULL
;
312 if (c
->m_rt
) c
->m_rt
->m_lf
= NULL
;
314 // append to the list of available boxes
315 c
->m_next
= m_available
;
319 // --------------------------------------------------------------------------
321 // --------------------------------------------------------------------------
323 // Post eight cells to the cell arrays (changed cells only)
324 void Life::DoLine(wxInt32 i
, wxInt32 j
, wxUint32 live
, wxUint32 old
)
326 wxUint32 diff
= (live
^ old
) & 0x000000ff;
330 for (wxInt32 k
= 8; k
; k
--, i
++)
334 m_cells
[m_ncells
].i
= i
;
335 m_cells
[m_ncells
].j
= j
;
343 // Post eight cells to the cell arrays (alive cells only)
344 void Life::DoLine(wxInt32 i
, wxInt32 j
, wxUint32 live
)
346 if (! (live
& 0x000000ff)) return;
348 for (wxInt32 k
= 8; k
; k
--, i
++)
352 m_cells
[m_ncells
].i
= i
;
353 m_cells
[m_ncells
].j
= j
;
360 void Life::BeginFind(wxInt32 i0
, wxInt32 j0
, wxInt32 i1
, wxInt32 j1
, bool changed
)
362 // TODO: optimize for the case where the maximum number of
363 // cellboxes that fit in the specified viewport is smaller
364 // than the current total of boxes; iterating over the list
365 // should then be faster than searching in the hash table.
367 m_i0
= m_i
= i0
& 0xfffffff8;
368 m_j0
= m_j
= j0
& 0xfffffff8;
369 m_i1
= (i1
+ 7) & 0xfffffff8;
370 m_j1
= (j1
+ 7) & 0xfffffff8;
376 bool Life::FindMore(Cell
*cells
[], size_t *ncells
)
384 for ( ; m_j
<= m_j1
; m_j
+= 8, m_i
= m_i0
)
385 for ( ; m_i
<= m_i1
; m_i
+= 8)
387 if ((c
= LinkBox(m_i
, m_j
, FALSE
)) == NULL
)
390 // check whether there is enough space left in the array
391 if (m_ncells
> (ARRAYSIZE
- 64))
397 DoLine(m_i
, m_j
, c
->m_live1
, c
->m_old1
);
398 DoLine(m_i
, m_j
+ 1, c
->m_live1
>> 8, c
->m_old1
>> 8 );
399 DoLine(m_i
, m_j
+ 2, c
->m_live1
>> 16, c
->m_old1
>> 16);
400 DoLine(m_i
, m_j
+ 3, c
->m_live1
>> 24, c
->m_old1
>> 24);
401 DoLine(m_i
, m_j
+ 4, c
->m_live2
, c
->m_old2
);
402 DoLine(m_i
, m_j
+ 5, c
->m_live2
>> 8, c
->m_old2
>> 8 );
403 DoLine(m_i
, m_j
+ 6, c
->m_live2
>> 16, c
->m_old2
>> 16);
404 DoLine(m_i
, m_j
+ 7, c
->m_live2
>> 24, c
->m_old2
>> 24);
409 for ( ; m_j
<= m_j1
; m_j
+= 8, m_i
= m_i0
)
410 for ( ; m_i
<= m_i1
; m_i
+= 8)
412 if ((c
= LinkBox(m_i
, m_j
, FALSE
)) == NULL
)
415 // check whether there is enough space left in the array
416 if (m_ncells
> (ARRAYSIZE
- 64))
422 DoLine(m_i
, m_j
, c
->m_live1
);
423 DoLine(m_i
, m_j
+ 1, c
->m_live1
>> 8 );
424 DoLine(m_i
, m_j
+ 2, c
->m_live1
>> 16);
425 DoLine(m_i
, m_j
+ 3, c
->m_live1
>> 24);
426 DoLine(m_i
, m_j
+ 4, c
->m_live2
);
427 DoLine(m_i
, m_j
+ 5, c
->m_live2
>> 8 );
428 DoLine(m_i
, m_j
+ 6, c
->m_live2
>> 16);
429 DoLine(m_i
, m_j
+ 7, c
->m_live2
>> 24);
438 // --------------------------------------------------------------------------
440 // --------------------------------------------------------------------------
446 // Advance one step in evolution :-)
450 CellBox
*c
, *up
, *dn
, *lf
, *rt
;
451 wxUint32 t1
, t2
, t3
, t4
;
452 bool changed
= FALSE
;
457 // Compute neighbours of each cell
459 // WARNING: unrolled loops and lengthy code follows!
465 if (! (c
->m_live1
|| c
->m_live2
))
476 t1
= c
->m_live1
& 0x000000ff;
481 up
= LinkBox(c
->m_x
, c
->m_y
- 8);
487 c
->m_on
[0] += g_tab2
[t1
];
491 t1
= (c
->m_live2
& 0xff000000) >> 24;
496 dn
= LinkBox(c
->m_x
, c
->m_y
+ 8);
502 c
->m_on
[7] += g_tab2
[t1
];
513 lf
= LinkBox(c
->m_x
- 8, c
->m_y
);
520 lf
->m_up
= LinkBox(c
->m_x
- 8, c
->m_y
- 8);
523 lf
->m_up
->m_on
[7] += 0x10000000;
524 lf
->m_on
[0] += 0x10000000;
525 lf
->m_on
[1] += 0x10000000;
529 lf
->m_on
[0] += 0x10000000;
530 lf
->m_on
[1] += 0x10000000;
531 lf
->m_on
[2] += 0x10000000;
535 lf
->m_on
[1] += 0x10000000;
536 lf
->m_on
[2] += 0x10000000;
537 lf
->m_on
[3] += 0x10000000;
541 lf
->m_on
[2] += 0x10000000;
542 lf
->m_on
[3] += 0x10000000;
543 lf
->m_on
[4] += 0x10000000;
550 lf
= LinkBox(c
->m_x
- 8, c
->m_y
);
555 lf
->m_on
[3] += 0x10000000;
556 lf
->m_on
[4] += 0x10000000;
557 lf
->m_on
[5] += 0x10000000;
561 lf
->m_on
[4] += 0x10000000;
562 lf
->m_on
[5] += 0x10000000;
563 lf
->m_on
[6] += 0x10000000;
567 lf
->m_on
[5] += 0x10000000;
568 lf
->m_on
[6] += 0x10000000;
569 lf
->m_on
[7] += 0x10000000;
575 lf
->m_dn
= LinkBox(c
->m_x
- 8, c
->m_y
+ 8);
578 lf
->m_on
[6] += 0x10000000;
579 lf
->m_on
[7] += 0x10000000;
580 lf
->m_dn
->m_on
[0] += 0x10000000;
589 rt
= LinkBox(c
->m_x
+ 8, c
->m_y
);
596 rt
->m_up
= LinkBox(c
->m_x
+ 8, c
->m_y
- 8);
599 rt
->m_up
->m_on
[7] += 0x00000001;
600 rt
->m_on
[0] += 0x00000001;
601 rt
->m_on
[1] += 0x00000001;
605 rt
->m_on
[0] += 0x00000001;
606 rt
->m_on
[1] += 0x00000001;
607 rt
->m_on
[2] += 0x00000001;
611 rt
->m_on
[1] += 0x00000001;
612 rt
->m_on
[2] += 0x00000001;
613 rt
->m_on
[3] += 0x00000001;
617 rt
->m_on
[2] += 0x00000001;
618 rt
->m_on
[3] += 0x00000001;
619 rt
->m_on
[4] += 0x00000001;
626 rt
= LinkBox(c
->m_x
+ 8, c
->m_y
);
631 rt
->m_on
[3] += 0x00000001;
632 rt
->m_on
[4] += 0x00000001;
633 rt
->m_on
[5] += 0x00000001;
637 rt
->m_on
[4] += 0x00000001;
638 rt
->m_on
[5] += 0x00000001;
639 rt
->m_on
[6] += 0x00000001;
643 rt
->m_on
[5] += 0x00000001;
644 rt
->m_on
[6] += 0x00000001;
645 rt
->m_on
[7] += 0x00000001;
651 rt
->m_dn
= LinkBox(c
->m_x
+ 8, c
->m_y
+ 8);
654 rt
->m_on
[6] += 0x00000001;
655 rt
->m_on
[7] += 0x00000001;
656 rt
->m_dn
->m_on
[0] += 0x00000001;
661 for (int i
= 1; i
<= 3; i
++)
663 t1
= ((c
->m_live1
) >> (i
* 8)) & 0x000000ff;
666 c
->m_on
[i
- 1] += g_tab1
[t1
];
667 c
->m_on
[i
] += g_tab2
[t1
];
668 c
->m_on
[i
+ 1] += g_tab1
[t1
];
671 for (int i
= 0; i
<= 2; i
++)
673 t1
= ((c
->m_live2
) >> (i
* 8)) & 0x000000ff;
676 c
->m_on
[i
+ 3] += g_tab1
[t1
];
677 c
->m_on
[i
+ 4] += g_tab2
[t1
];
678 c
->m_on
[i
+ 5] += g_tab1
[t1
];
692 // WARNING: to be optimized and unrolled soon.
701 for (int i
= 0; i
<= 3; i
++)
711 for (int j
= 0; j
< 8; j
++)
714 t4
= t3
& 0x0000000f;
716 if ((t4
== 3) || ((t4
== 2) && (t1
& 0x00000001)))
732 for (int i
= 4; i
<= 7; i
++)
742 for (int j
= 0; j
< 8; j
++)
745 t4
= t3
& 0x0000000f;
747 if ((t4
== 3) || ((t4
== 2) && (t1
& 0x00000001)))
760 // keep track of changes
761 changed
|= ((c
->m_live1
^ c
->m_old1
) || (c
->m_live2
^ c
->m_old2
));
763 // mark, and discard if necessary, dead boxes
764 if (c
->m_live1
|| c
->m_live2
)
771 CellBox
*aux
= c
->m_next
;
772 if (c
->m_dead
++ > MAXDEAD
)
782 // --------------------------------------------------------------------------
783 // Lookup tables - these will be generated on-the-fly soon.
784 // --------------------------------------------------------------------------
786 // This table converts from bits (like in live1, live2) to number
787 // of neighbors for each cell in the upper or lower row.
1049 // This table converts from bits (like in live1, live2) to number
1050 // of neighbors for each cell in the same row (excluding ourselves)