]>
git.saurik.com Git - wxWidgets.git/blob - demos/life/game.cpp
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 <string.h> // for memset
25 #define ARRAYSIZE 1024 // size of the static array for BeginFind & co.
26 #define ALLOCBOXES 16 // number of cellboxes to alloc at once
29 // ==========================================================================
31 // ==========================================================================
33 #define HASH(x, y) (((x >> 3) & 0x7f) << 7) + ((y >> 3) & 0x7f)
34 #define HASHSIZE 32768
42 inline bool IsAlive(int dx
, int dy
) const;
43 inline bool SetCell(int dx
, int dy
, bool alive
);
46 wxInt32 m_x
, m_y
; // position in universe
47 wxUint32 m_live1
, m_live2
; // alive cells (1 bit per cell)
48 wxUint32 m_old1
, m_old2
; // old values for m_live1, 2
49 wxUint32 m_on
[8]; // neighbouring info
50 wxUint32 m_dead
; // been dead for n generations
51 CellBox
*m_up
, *m_dn
, *m_lf
, *m_rt
; // neighbour CellBoxes
52 CellBox
*m_prev
, *m_next
; // in linked list
53 CellBox
*m_hprev
, *m_hnext
; // in hash table
58 // Returns whether cell dx, dy in this box is alive
60 bool CellBox::IsAlive(int dx
, int dy
) const
63 return (m_live2
& 1 << ((dy
- 4) * 8 + dx
));
65 return (m_live1
& 1 << ((dy
) * 8 + dx
));
69 // Sets cell dx, dy in this box to 'alive', returns TRUE if
70 // the previous value was different, FALSE if it was the same.
72 bool CellBox::SetCell(int dx
, int dy
, bool alive
)
74 if (IsAlive(dx
, dy
) != alive
)
77 m_live2
^= 1 << ((dy
- 4) * 8 + dx
);
79 m_live1
^= 1 << ((dy
) * 8 + dx
);
81 // reset this here to avoid updating problems
91 // ==========================================================================
93 // ==========================================================================
95 // --------------------------------------------------------------------------
97 // --------------------------------------------------------------------------
102 m_boxes
= new CellBox
*[HASHSIZE
];
105 for (int i
= 0; i
< HASHSIZE
; i
++)
108 m_cells
= new Cell
[ARRAYSIZE
];
123 // Clears the board, freeing all storage.
131 // clear the hash table pointers
132 for (int i
= 0; i
< HASHSIZE
; i
++)
145 // free available boxes
156 // --------------------------------------------------------------------------
157 // Test and set individual cells
158 // --------------------------------------------------------------------------
161 // Returns whether cell (x, y) is alive.
163 bool Life::IsAlive(wxInt32 x
, wxInt32 y
)
165 CellBox
*c
= LinkBox(x
, y
, FALSE
);
167 return (c
&& c
->IsAlive( x
- c
->m_x
, y
- c
->m_y
));
171 // Sets or clears cell (x, y), according to the 'alive' param.
173 void Life::SetCell(wxInt32 x
, wxInt32 y
, bool alive
)
175 CellBox
*c
= LinkBox(x
, y
);
176 wxUint32 dx
= x
- c
->m_x
;
177 wxUint32 dy
= y
- c
->m_y
;
179 if (c
->SetCell(dx
, dy
, alive
))
188 void Life::SetShape(const LifeShape
& shape
)
190 char *p
= shape
.m_data
;
192 int i0
= -(shape
.m_width
/ 2);
193 int j0
= -(shape
.m_height
/ 2);
194 int i1
= i0
+ shape
.m_width
- 1;
195 int j1
= j0
+ shape
.m_height
- 1;
198 for (int j
= j0
; j
<= j1
; j
++)
199 for (int i
= i0
; i
<= i1
; i
++)
200 SetCell(i
, j
, *(p
++) == '*');
203 // --------------------------------------------------------------------------
204 // Cellbox management functions
205 // --------------------------------------------------------------------------
208 // Creates a box in x, y, either taking it from the list
209 // of available boxes, or allocating a new one.
211 CellBox
* Life::CreateBox(wxInt32 x
, wxInt32 y
, wxUint32 hv
)
215 // if there are no available boxes, alloc a few more
217 for (int i
= 1; i
<= ALLOCBOXES
; i
++)
223 // TODO: handle memory errors. Note that right now, if we
224 // couldn't allocate at least one cellbox, we will crash
225 // before leaving CreateBox(). Probably we should try to
226 // allocate some boxes *before* the m_available list goes
227 // empty, so that we have a margin to handle errors
229 wxLogFatalError(_("Out of memory! Aborting..."));
234 c
->m_next
= m_available
;
238 // take a cellbox from the list of available boxes
240 m_available
= c
->m_next
;
243 memset((void *) c
, 0, sizeof(CellBox
));
247 // insert c in the list
250 if (c
->m_next
) c
->m_next
->m_prev
= c
;
252 // insert c in the hash table
253 c
->m_hnext
= m_boxes
[hv
];
255 if (c
->m_hnext
) c
->m_hnext
->m_hprev
= c
;
261 // Returns a pointer to the box (x, y); if it didn't exist yet,
262 // it returns NULL or creates a new one, depending on the value
263 // of the 'create' parameter.
265 CellBox
* Life::LinkBox(wxInt32 x
, wxInt32 y
, bool create
)
274 // search in the hash table
275 for (c
= m_boxes
[hv
]; c
; c
= c
->m_hnext
)
276 if ((c
->m_x
== x
) && (c
->m_y
== y
)) return c
;
278 // if not found, and (create == TRUE), create a new one
279 return create
? CreateBox(x
, y
, hv
) : NULL
;
283 // Removes this box from the list and the hash table and
284 // puts it in the list of available boxes.
286 void Life::KillBox(CellBox
*c
)
288 wxUint32 hv
= HASH(c
->m_x
, c
->m_y
);
290 // remove from the list
292 c
->m_prev
->m_next
= c
->m_next
;
296 // remove from the hash table
297 if (c
!= m_boxes
[hv
])
298 c
->m_hprev
->m_hnext
= c
->m_hnext
;
300 m_boxes
[hv
] = c
->m_hnext
;
303 if (c
->m_next
) c
->m_next
->m_prev
= c
->m_prev
;
304 if (c
->m_hnext
) c
->m_hnext
->m_hprev
= c
->m_hprev
;
305 if (c
->m_up
) c
->m_up
->m_dn
= NULL
;
306 if (c
->m_dn
) c
->m_dn
->m_up
= NULL
;
307 if (c
->m_lf
) c
->m_lf
->m_rt
= NULL
;
308 if (c
->m_rt
) c
->m_rt
->m_lf
= NULL
;
310 // append to the list of available boxes
311 c
->m_next
= m_available
;
315 // --------------------------------------------------------------------------
317 // --------------------------------------------------------------------------
319 // Post eight cells to the cell arrays (changed cells only)
320 void Life::DoLine(wxInt32 i
, wxInt32 j
, wxUint32 live
, wxUint32 old
)
322 wxUint32 diff
= (live
^ old
) & 0x000000ff;
326 for (wxInt32 k
= 8; k
; k
--, i
++)
330 m_cells
[m_ncells
].i
= i
;
331 m_cells
[m_ncells
].j
= j
;
339 // Post eight cells to the cell arrays (alive cells only)
340 void Life::DoLine(wxInt32 i
, wxInt32 j
, wxUint32 live
)
342 if (! (live
& 0x000000ff)) return;
344 for (wxInt32 k
= 8; k
; k
--, i
++)
348 m_cells
[m_ncells
].i
= i
;
349 m_cells
[m_ncells
].j
= j
;
356 void Life::BeginFind(wxInt32 i0
, wxInt32 j0
, wxInt32 i1
, wxInt32 j1
, bool changed
)
358 // TODO: optimize for the case where the maximum number of
359 // cellboxes that fit in the specified viewport is smaller
360 // than the current total of boxes; iterating over the list
361 // should then be faster than searching in the hash table.
363 m_i0
= m_i
= i0
& 0xfffffff8;
364 m_j0
= m_j
= j0
& 0xfffffff8;
365 m_i1
= (i1
+ 7) & 0xfffffff8;
366 m_j1
= (j1
+ 7) & 0xfffffff8;
372 bool Life::FindMore(Cell
*cells
[], size_t *ncells
)
380 for ( ; m_j
<= m_j1
; m_j
+= 8, m_i
= m_i0
)
381 for ( ; m_i
<= m_i1
; m_i
+= 8)
383 if ((c
= LinkBox(m_i
, m_j
, FALSE
)) == NULL
)
386 // check whether there is enough space left in the array
387 if (m_ncells
> (ARRAYSIZE
- 64))
393 DoLine(m_i
, m_j
, c
->m_live1
, c
->m_old1
);
394 DoLine(m_i
, m_j
+ 1, c
->m_live1
>> 8, c
->m_old1
>> 8 );
395 DoLine(m_i
, m_j
+ 2, c
->m_live1
>> 16, c
->m_old1
>> 16);
396 DoLine(m_i
, m_j
+ 3, c
->m_live1
>> 24, c
->m_old1
>> 24);
397 DoLine(m_i
, m_j
+ 4, c
->m_live2
, c
->m_old2
);
398 DoLine(m_i
, m_j
+ 5, c
->m_live2
>> 8, c
->m_old2
>> 8 );
399 DoLine(m_i
, m_j
+ 6, c
->m_live2
>> 16, c
->m_old2
>> 16);
400 DoLine(m_i
, m_j
+ 7, c
->m_live2
>> 24, c
->m_old2
>> 24);
405 for ( ; m_j
<= m_j1
; m_j
+= 8, m_i
= m_i0
)
406 for ( ; m_i
<= m_i1
; m_i
+= 8)
408 if ((c
= LinkBox(m_i
, m_j
, FALSE
)) == NULL
)
411 // check whether there is enough space left in the array
412 if (m_ncells
> (ARRAYSIZE
- 64))
418 DoLine(m_i
, m_j
, c
->m_live1
);
419 DoLine(m_i
, m_j
+ 1, c
->m_live1
>> 8 );
420 DoLine(m_i
, m_j
+ 2, c
->m_live1
>> 16);
421 DoLine(m_i
, m_j
+ 3, c
->m_live1
>> 24);
422 DoLine(m_i
, m_j
+ 4, c
->m_live2
);
423 DoLine(m_i
, m_j
+ 5, c
->m_live2
>> 8 );
424 DoLine(m_i
, m_j
+ 6, c
->m_live2
>> 16);
425 DoLine(m_i
, m_j
+ 7, c
->m_live2
>> 24);
434 // --------------------------------------------------------------------------
436 // --------------------------------------------------------------------------
438 extern unsigned char *g_tab
;
443 // Advance one step in evolution :-)
447 CellBox
*c
, *up
, *dn
, *lf
, *rt
;
448 wxUint32 t1
, t2
, t3
, t4
;
449 bool changed
= FALSE
;
454 // Compute neighbours of each cell
456 // WARNING: unrolled loops and lengthy code follows!
462 if (! (c
->m_live1
|| c
->m_live2
))
473 t1
= c
->m_live1
& 0x000000ff;
478 up
= LinkBox(c
->m_x
, c
->m_y
- 8);
484 c
->m_on
[0] += g_tab2
[t1
];
488 t1
= (c
->m_live2
& 0xff000000) >> 24;
493 dn
= LinkBox(c
->m_x
, c
->m_y
+ 8);
499 c
->m_on
[7] += g_tab2
[t1
];
510 lf
= LinkBox(c
->m_x
- 8, c
->m_y
);
517 lf
->m_up
= LinkBox(c
->m_x
- 8, c
->m_y
- 8);
520 lf
->m_up
->m_on
[7] += 0x10000000;
521 lf
->m_on
[0] += 0x10000000;
522 lf
->m_on
[1] += 0x10000000;
526 lf
->m_on
[0] += 0x10000000;
527 lf
->m_on
[1] += 0x10000000;
528 lf
->m_on
[2] += 0x10000000;
532 lf
->m_on
[1] += 0x10000000;
533 lf
->m_on
[2] += 0x10000000;
534 lf
->m_on
[3] += 0x10000000;
538 lf
->m_on
[2] += 0x10000000;
539 lf
->m_on
[3] += 0x10000000;
540 lf
->m_on
[4] += 0x10000000;
547 lf
= LinkBox(c
->m_x
- 8, c
->m_y
);
552 lf
->m_on
[3] += 0x10000000;
553 lf
->m_on
[4] += 0x10000000;
554 lf
->m_on
[5] += 0x10000000;
558 lf
->m_on
[4] += 0x10000000;
559 lf
->m_on
[5] += 0x10000000;
560 lf
->m_on
[6] += 0x10000000;
564 lf
->m_on
[5] += 0x10000000;
565 lf
->m_on
[6] += 0x10000000;
566 lf
->m_on
[7] += 0x10000000;
572 lf
->m_dn
= LinkBox(c
->m_x
- 8, c
->m_y
+ 8);
575 lf
->m_on
[6] += 0x10000000;
576 lf
->m_on
[7] += 0x10000000;
577 lf
->m_dn
->m_on
[0] += 0x10000000;
586 rt
= LinkBox(c
->m_x
+ 8, c
->m_y
);
593 rt
->m_up
= LinkBox(c
->m_x
+ 8, c
->m_y
- 8);
596 rt
->m_up
->m_on
[7] += 0x00000001;
597 rt
->m_on
[0] += 0x00000001;
598 rt
->m_on
[1] += 0x00000001;
602 rt
->m_on
[0] += 0x00000001;
603 rt
->m_on
[1] += 0x00000001;
604 rt
->m_on
[2] += 0x00000001;
608 rt
->m_on
[1] += 0x00000001;
609 rt
->m_on
[2] += 0x00000001;
610 rt
->m_on
[3] += 0x00000001;
614 rt
->m_on
[2] += 0x00000001;
615 rt
->m_on
[3] += 0x00000001;
616 rt
->m_on
[4] += 0x00000001;
623 rt
= LinkBox(c
->m_x
+ 8, c
->m_y
);
628 rt
->m_on
[3] += 0x00000001;
629 rt
->m_on
[4] += 0x00000001;
630 rt
->m_on
[5] += 0x00000001;
634 rt
->m_on
[4] += 0x00000001;
635 rt
->m_on
[5] += 0x00000001;
636 rt
->m_on
[6] += 0x00000001;
640 rt
->m_on
[5] += 0x00000001;
641 rt
->m_on
[6] += 0x00000001;
642 rt
->m_on
[7] += 0x00000001;
648 rt
->m_dn
= LinkBox(c
->m_x
+ 8, c
->m_y
+ 8);
651 rt
->m_on
[6] += 0x00000001;
652 rt
->m_on
[7] += 0x00000001;
653 rt
->m_dn
->m_on
[0] += 0x00000001;
659 for (i
= 1; i
<= 3; i
++)
661 t1
= ((c
->m_live1
) >> (i
* 8)) & 0x000000ff;
664 c
->m_on
[i
- 1] += g_tab1
[t1
];
665 c
->m_on
[i
] += g_tab2
[t1
];
666 c
->m_on
[i
+ 1] += g_tab1
[t1
];
669 for (i
= 0; i
<= 2; i
++)
671 t1
= ((c
->m_live2
) >> (i
* 8)) & 0x000000ff;
674 c
->m_on
[i
+ 3] += g_tab1
[t1
];
675 c
->m_on
[i
+ 4] += g_tab2
[t1
];
676 c
->m_on
[i
+ 5] += g_tab1
[t1
];
701 t1
|= g_tab
[ ((t4
& 0x0000ffff) << 4 ) + ((t3
) & 0xf) ];
702 t1
|= g_tab
[ ((t4
& 0xffff0000) >> 12) + ((t3
>> 4 ) & 0xf) ] << 4;
704 t1
|= g_tab
[ ((t4
& 0x0000ffff) << 4 ) + ((t3
>> 8 ) & 0xf) ] << 8;
705 t1
|= g_tab
[ ((t4
& 0xffff0000) >> 12) + ((t3
>> 12) & 0xf) ] << 12;
707 t1
|= g_tab
[ ((t4
& 0x0000ffff) << 4 ) + ((t3
>> 16) & 0xf) ] << 16;
708 t1
|= g_tab
[ ((t4
& 0xffff0000) >> 12) + ((t3
>> 20) & 0xf) ] << 20;
710 t1
|= g_tab
[ ((t4
& 0x0000ffff) << 4 ) + ((t3
>> 24) & 0xf) ] << 24;
711 t1
|= g_tab
[ ((t4
& 0xffff0000) >> 12) + ((t3
>> 28) & 0xf) ] << 28;
717 t2
|= g_tab
[ ((t4
& 0x0000ffff) << 4 ) + ((t3
) & 0xf) ];
718 t2
|= g_tab
[ ((t4
& 0xffff0000) >> 12) + ((t3
>> 4 ) & 0xf) ] << 4;
720 t2
|= g_tab
[ ((t4
& 0x0000ffff) << 4 ) + ((t3
>> 8 ) & 0xf) ] << 8;
721 t2
|= g_tab
[ ((t4
& 0xffff0000) >> 12) + ((t3
>> 12) & 0xf) ] << 12;
723 t2
|= g_tab
[ ((t4
& 0x0000ffff) << 4 ) + ((t3
>> 16) & 0xf) ] << 16;
724 t2
|= g_tab
[ ((t4
& 0xffff0000) >> 12) + ((t3
>> 20) & 0xf) ] << 20;
726 t2
|= g_tab
[ ((t4
& 0x0000ffff) << 4 ) + ((t3
>> 24) & 0xf) ] << 24;
727 t2
|= g_tab
[ ((t4
& 0xffff0000) >> 12) + ((t3
>> 28) & 0xf) ] << 28;
729 c
->m_on
[0] = c
->m_on
[1] = c
->m_on
[2] = c
->m_on
[3] =
730 c
->m_on
[4] = c
->m_on
[5] = c
->m_on
[6] = c
->m_on
[7] = 0;
734 // count alive cells (TODO: find a better way to do this)
735 for (int i
= 0; i
< 32; i
++)
737 if (t1
& (1 << i
)) m_numcells
++;
738 if (t2
& (1 << i
)) m_numcells
++;
741 changed
|= ((t1
^ c
->m_old1
) || (t2
^ c
->m_old2
));
743 // mark, and discard if necessary, dead boxes
751 CellBox
*aux
= c
->m_next
;
752 if (c
->m_dead
++ > MAXDEAD
)
762 // ==========================================================================
764 // ==========================================================================
766 // A module to pregenerate lookup tables without having to do it
767 // from the application.
769 class LifeModule
: public wxModule
771 DECLARE_DYNAMIC_CLASS(LifeModule
)
779 IMPLEMENT_DYNAMIC_CLASS(LifeModule
, wxModule
)
781 bool LifeModule::OnInit()
784 g_tab
= new unsigned char [0xfffff];
786 if (!g_tab
) return FALSE
;
788 for (wxUint32 i
= 0; i
< 0xfffff; i
++)
790 wxUint32 val
= i
>> 4;
791 wxUint32 old
= i
& 0x0000f;
794 for (int j
= 0; j
< 4; j
++)
798 if (((val
& 0xf) == 3) || (((val
& 0xf) == 2) && (old
& 0x1)))
805 g_tab
[i
] = (unsigned char) live
;
811 void LifeModule::OnExit()
817 // This table converts from number of neighbors (like in on[]) to
818 // bits, for a set of four cells. It takes as index a five-digit
819 // hexadecimal value (0xNNNNB) where Ns hold number of neighbors
820 // for each cell and B holds their previous state.
822 unsigned char *g_tab
;
824 // This table converts from bits (like in live1, live2) to number
825 // of neighbors for each cell in the upper or lower row.
1087 // This table converts from bits (like in live1, live2) to number
1088 // of neighbors for each cell in the same row (excluding ourselves)