]>
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 /////////////////////////////////////////////////////////////////////////////
13 #error "Sorry, Life! will not work in 16-bit Windows"
16 // ==========================================================================
17 // headers, declarations, constants
18 // ==========================================================================
21 #pragma implementation "game.h"
24 // For compilers that support precompilation, includes "wx/wx.h".
25 #include "wx/wxprec.h"
36 #include "wx/module.h"
39 #include <string.h> // for memset
42 #define ARRAYSIZE 1024 // static array for BeginFind & co.
43 #define ALLOCBOXES 16 // number of cellboxes to alloc at once
44 #define MAXDEAD 8 // tics before removing cellbox from list
47 // ==========================================================================
49 // ==========================================================================
51 #define HASH(x, y) (((x >> 3) & 0x7f) << 7) + ((y >> 3) & 0x7f)
53 #define HASHSIZE 32768 // hash table size (do not change!)
54 #define CELLBOX 8 // cells in a cellbox (do not change!)
61 inline bool IsAlive(int dx
, int dy
) const;
62 inline bool SetCell(int dx
, int dy
, bool alive
);
65 wxInt32 m_x
, m_y
; // position in universe
66 wxUint32 m_live1
, m_live2
; // alive cells (1 bit per cell)
67 wxUint32 m_old1
, m_old2
; // old values for m_live1, 2
68 wxUint32 m_on
[8]; // neighbouring info
69 wxUint32 m_dead
; // been dead for n generations
70 CellBox
*m_up
, *m_dn
, *m_lf
, *m_rt
; // neighbour CellBoxes
71 CellBox
*m_prev
, *m_next
; // in linked list
72 CellBox
*m_hprev
, *m_hnext
; // in hash table
77 // Returns whether cell dx, dy in this box is alive
79 bool CellBox::IsAlive(int dx
, int dy
) const
82 return (m_live2
& 1 << ((dy
- 4) * 8 + dx
));
84 return (m_live1
& 1 << ((dy
) * 8 + dx
));
88 // Sets cell dx, dy in this box to 'alive', returns TRUE if
89 // the previous value was different, FALSE if it was the same.
91 bool CellBox::SetCell(int dx
, int dy
, bool alive
)
93 if (IsAlive(dx
, dy
) != alive
)
96 m_live2
^= 1 << ((dy
- 4) * 8 + dx
);
98 m_live1
^= 1 << ((dy
) * 8 + dx
);
100 // reset this here to avoid updating problems
110 // ==========================================================================
112 // ==========================================================================
114 // --------------------------------------------------------------------------
116 // --------------------------------------------------------------------------
121 m_boxes
= new CellBox
*[HASHSIZE
];
124 for (int i
= 0; i
< HASHSIZE
; i
++)
127 m_cells
= new Cell
[ARRAYSIZE
];
142 // Clears the board, freeing all storage.
150 // clear the hash table pointers
151 for (int i
= 0; i
< HASHSIZE
; i
++)
164 // free available boxes
175 // --------------------------------------------------------------------------
176 // Test and set individual cells
177 // --------------------------------------------------------------------------
180 // Returns whether cell (x, y) is alive.
182 bool Life::IsAlive(wxInt32 x
, wxInt32 y
)
184 CellBox
*c
= LinkBox(x
, y
, FALSE
);
186 return (c
&& c
->IsAlive( x
- c
->m_x
, y
- c
->m_y
));
190 // Sets or clears cell (x, y), according to the 'alive' param.
192 void Life::SetCell(wxInt32 x
, wxInt32 y
, bool alive
)
194 CellBox
*c
= LinkBox(x
, y
);
195 wxUint32 dx
= x
- c
->m_x
;
196 wxUint32 dy
= y
- c
->m_y
;
198 if (c
->SetCell(dx
, dy
, alive
))
207 void Life::SetShape(const LifeShape
& shape
)
209 char *p
= shape
.m_data
;
211 int i0
= -(shape
.m_width
/ 2);
212 int j0
= -(shape
.m_height
/ 2);
213 int i1
= i0
+ shape
.m_width
- 1;
214 int j1
= j0
+ shape
.m_height
- 1;
217 for (int j
= j0
; j
<= j1
; j
++)
218 for (int i
= i0
; i
<= i1
; i
++)
219 SetCell(i
, j
, *(p
++) == '*');
222 // --------------------------------------------------------------------------
223 // Cellbox management functions
224 // --------------------------------------------------------------------------
227 // Creates a box in x, y, either taking it from the list
228 // of available boxes, or allocating a new one.
230 CellBox
* Life::CreateBox(wxInt32 x
, wxInt32 y
, wxUint32 hv
)
234 // if there are no available boxes, alloc a few more
236 for (int i
= 1; i
<= ALLOCBOXES
; i
++)
242 // TODO: handle memory errors. Note that right now, if we
243 // couldn't allocate at least one cellbox, we will crash
244 // before leaving CreateBox(). Probably we should try to
245 // allocate some boxes *before* the m_available list goes
246 // empty, so that we have a margin to handle errors
248 wxLogFatalError(_("Out of memory! Aborting..."));
253 c
->m_next
= m_available
;
257 // take a cellbox from the list of available boxes
259 m_available
= c
->m_next
;
262 memset((void *) c
, 0, sizeof(CellBox
));
266 // insert c in the list
269 if (c
->m_next
) c
->m_next
->m_prev
= c
;
271 // insert c in the hash table
272 c
->m_hnext
= m_boxes
[hv
];
274 if (c
->m_hnext
) c
->m_hnext
->m_hprev
= c
;
280 // Returns a pointer to the box (x, y); if it didn't exist yet,
281 // it returns NULL or creates a new one, depending on the value
282 // of the 'create' parameter.
284 CellBox
* Life::LinkBox(wxInt32 x
, wxInt32 y
, bool create
)
293 // search in the hash table
294 for (c
= m_boxes
[hv
]; c
; c
= c
->m_hnext
)
295 if ((c
->m_x
== x
) && (c
->m_y
== y
)) return c
;
297 // if not found, and (create == TRUE), create a new one
298 return create
? CreateBox(x
, y
, hv
) : (CellBox
*) NULL
;
302 // Removes this box from the list and the hash table and
303 // puts it in the list of available boxes.
305 void Life::KillBox(CellBox
*c
)
307 wxUint32 hv
= HASH(c
->m_x
, c
->m_y
);
309 // remove from the list
311 c
->m_prev
->m_next
= c
->m_next
;
315 // remove from the hash table
316 if (c
!= m_boxes
[hv
])
317 c
->m_hprev
->m_hnext
= c
->m_hnext
;
319 m_boxes
[hv
] = c
->m_hnext
;
322 if (c
->m_next
) c
->m_next
->m_prev
= c
->m_prev
;
323 if (c
->m_hnext
) c
->m_hnext
->m_hprev
= c
->m_hprev
;
324 if (c
->m_up
) c
->m_up
->m_dn
= NULL
;
325 if (c
->m_dn
) c
->m_dn
->m_up
= NULL
;
326 if (c
->m_lf
) c
->m_lf
->m_rt
= NULL
;
327 if (c
->m_rt
) c
->m_rt
->m_lf
= NULL
;
329 // append to the list of available boxes
330 c
->m_next
= m_available
;
334 // --------------------------------------------------------------------------
336 // --------------------------------------------------------------------------
338 Cell
Life::FindCenter()
347 for (c
= m_head
; c
; c
= c
->m_next
)
357 i
= (i
/ n
) + CELLBOX
/ 2;
358 j
= (j
/ n
) + CELLBOX
/ 2;
362 cell
.i
= (wxInt32
) i
;
363 cell
.j
= (wxInt32
) j
;
367 Cell
Life::FindNorth()
369 wxInt32 i
= 0, j
= 0;
373 for (c
= m_head
; c
; c
= c
->m_next
)
374 if (!c
->m_dead
&& ((first
) || (c
->m_y
< j
)))
382 cell
.i
= first
? 0 : i
+ CELLBOX
/ 2;
383 cell
.j
= first
? 0 : j
+ CELLBOX
/ 2;
387 Cell
Life::FindSouth()
389 wxInt32 i
= 0, j
= 0;
393 for (c
= m_head
; c
; c
= c
->m_next
)
394 if (!c
->m_dead
&& ((first
) || (c
->m_y
> j
)))
402 cell
.i
= first
? 0 : i
+ CELLBOX
/ 2;
403 cell
.j
= first
? 0 : j
+ CELLBOX
/ 2;
407 Cell
Life::FindWest()
409 wxInt32 i
= 0, j
= 0;
413 for (c
= m_head
; c
; c
= c
->m_next
)
414 if (!c
->m_dead
&& ((first
) || (c
->m_x
< i
)))
422 cell
.i
= first
? 0 : i
+ CELLBOX
/ 2;
423 cell
.j
= first
? 0 : j
+ CELLBOX
/ 2;
427 Cell
Life::FindEast()
429 wxInt32 i
= 0, j
= 0;
433 for (c
= m_head
; c
; c
= c
->m_next
)
434 if (!c
->m_dead
&& ((first
) || (c
->m_x
> i
)))
442 cell
.i
= first
? 0 : i
+ CELLBOX
/ 2;
443 cell
.j
= first
? 0 : j
+ CELLBOX
/ 2;
447 // --------------------------------------------------------------------------
449 // --------------------------------------------------------------------------
452 // Post eight cells to the cell arrays - leave out the fourth
453 // argument (or pass 0, the default value) to post alive cells
454 // only, else it will post cells which have changed.
456 void Life::DoLine(wxInt32 i
, wxInt32 j
, wxUint32 live
, wxUint32 old
)
458 wxUint32 diff
= (live
^ old
) & 0xff;
462 for (wxInt32 k
= 8; k
; k
--, i
++)
466 m_cells
[m_ncells
].i
= i
;
467 m_cells
[m_ncells
].j
= j
;
474 void Life::BeginFind(wxInt32 i0
, wxInt32 j0
, wxInt32 i1
, wxInt32 j1
, bool changed
)
476 // TODO: optimize for the case where the maximum number of
477 // cellboxes that fit in the specified viewport is smaller
478 // than the current total of boxes; iterating over the list
479 // should then be faster than searching in the hash table.
481 m_i0
= m_i
= i0
& 0xfffffff8;
482 m_j0
= m_j
= j0
& 0xfffffff8;
483 m_i1
= (i1
+ 7) & 0xfffffff8;
484 m_j1
= (j1
+ 7) & 0xfffffff8;
490 bool Life::FindMore(Cell
*cells
[], size_t *ncells
)
498 for ( ; m_j
<= m_j1
; m_j
+= 8, m_i
= m_i0
)
499 for ( ; m_i
<= m_i1
; m_i
+= 8)
501 if ((c
= LinkBox(m_i
, m_j
, FALSE
)) == NULL
)
504 // check whether there is enough space left in the array
505 if (m_ncells
> (ARRAYSIZE
- 64))
511 DoLine(m_i
, m_j
, c
->m_live1
, c
->m_old1
);
512 DoLine(m_i
, m_j
+ 1, c
->m_live1
>> 8, c
->m_old1
>> 8 );
513 DoLine(m_i
, m_j
+ 2, c
->m_live1
>> 16, c
->m_old1
>> 16);
514 DoLine(m_i
, m_j
+ 3, c
->m_live1
>> 24, c
->m_old1
>> 24);
515 DoLine(m_i
, m_j
+ 4, c
->m_live2
, c
->m_old2
);
516 DoLine(m_i
, m_j
+ 5, c
->m_live2
>> 8, c
->m_old2
>> 8 );
517 DoLine(m_i
, m_j
+ 6, c
->m_live2
>> 16, c
->m_old2
>> 16);
518 DoLine(m_i
, m_j
+ 7, c
->m_live2
>> 24, c
->m_old2
>> 24);
523 for ( ; m_j
<= m_j1
; m_j
+= 8, m_i
= m_i0
)
524 for ( ; m_i
<= m_i1
; m_i
+= 8)
526 if ((c
= LinkBox(m_i
, m_j
, FALSE
)) == NULL
)
529 // check whether there is enough space left in the array
530 if (m_ncells
> (ARRAYSIZE
- 64))
536 DoLine(m_i
, m_j
, c
->m_live1
);
537 DoLine(m_i
, m_j
+ 1, c
->m_live1
>> 8 );
538 DoLine(m_i
, m_j
+ 2, c
->m_live1
>> 16);
539 DoLine(m_i
, m_j
+ 3, c
->m_live1
>> 24);
540 DoLine(m_i
, m_j
+ 4, c
->m_live2
);
541 DoLine(m_i
, m_j
+ 5, c
->m_live2
>> 8 );
542 DoLine(m_i
, m_j
+ 6, c
->m_live2
>> 16);
543 DoLine(m_i
, m_j
+ 7, c
->m_live2
>> 24);
552 // --------------------------------------------------------------------------
554 // --------------------------------------------------------------------------
556 extern unsigned char *g_tab
;
561 // Advance one step in evolution :-)
565 CellBox
*c
, *up
, *dn
, *lf
, *rt
;
566 wxUint32 t1
, t2
, t3
, t4
;
567 bool changed
= FALSE
;
572 // Compute neighbours of each cell
574 // WARNING: unrolled loops and lengthy code follows!
580 if (! (c
->m_live1
|| c
->m_live2
))
591 t1
= c
->m_live1
& 0x000000ff;
596 up
= LinkBox(c
->m_x
, c
->m_y
- 8);
602 c
->m_on
[0] += g_tab2
[t1
];
606 t1
= (c
->m_live2
& 0xff000000) >> 24;
611 dn
= LinkBox(c
->m_x
, c
->m_y
+ 8);
617 c
->m_on
[7] += g_tab2
[t1
];
628 lf
= LinkBox(c
->m_x
- 8, c
->m_y
);
635 lf
->m_up
= LinkBox(c
->m_x
- 8, c
->m_y
- 8);
638 lf
->m_up
->m_on
[7] += 0x10000000;
639 lf
->m_on
[0] += 0x10000000;
640 lf
->m_on
[1] += 0x10000000;
644 lf
->m_on
[0] += 0x10000000;
645 lf
->m_on
[1] += 0x10000000;
646 lf
->m_on
[2] += 0x10000000;
650 lf
->m_on
[1] += 0x10000000;
651 lf
->m_on
[2] += 0x10000000;
652 lf
->m_on
[3] += 0x10000000;
656 lf
->m_on
[2] += 0x10000000;
657 lf
->m_on
[3] += 0x10000000;
658 lf
->m_on
[4] += 0x10000000;
665 lf
= LinkBox(c
->m_x
- 8, c
->m_y
);
670 lf
->m_on
[3] += 0x10000000;
671 lf
->m_on
[4] += 0x10000000;
672 lf
->m_on
[5] += 0x10000000;
676 lf
->m_on
[4] += 0x10000000;
677 lf
->m_on
[5] += 0x10000000;
678 lf
->m_on
[6] += 0x10000000;
682 lf
->m_on
[5] += 0x10000000;
683 lf
->m_on
[6] += 0x10000000;
684 lf
->m_on
[7] += 0x10000000;
690 lf
->m_dn
= LinkBox(c
->m_x
- 8, c
->m_y
+ 8);
693 lf
->m_on
[6] += 0x10000000;
694 lf
->m_on
[7] += 0x10000000;
695 lf
->m_dn
->m_on
[0] += 0x10000000;
704 rt
= LinkBox(c
->m_x
+ 8, c
->m_y
);
711 rt
->m_up
= LinkBox(c
->m_x
+ 8, c
->m_y
- 8);
714 rt
->m_up
->m_on
[7] += 0x00000001;
715 rt
->m_on
[0] += 0x00000001;
716 rt
->m_on
[1] += 0x00000001;
720 rt
->m_on
[0] += 0x00000001;
721 rt
->m_on
[1] += 0x00000001;
722 rt
->m_on
[2] += 0x00000001;
726 rt
->m_on
[1] += 0x00000001;
727 rt
->m_on
[2] += 0x00000001;
728 rt
->m_on
[3] += 0x00000001;
732 rt
->m_on
[2] += 0x00000001;
733 rt
->m_on
[3] += 0x00000001;
734 rt
->m_on
[4] += 0x00000001;
741 rt
= LinkBox(c
->m_x
+ 8, c
->m_y
);
746 rt
->m_on
[3] += 0x00000001;
747 rt
->m_on
[4] += 0x00000001;
748 rt
->m_on
[5] += 0x00000001;
752 rt
->m_on
[4] += 0x00000001;
753 rt
->m_on
[5] += 0x00000001;
754 rt
->m_on
[6] += 0x00000001;
758 rt
->m_on
[5] += 0x00000001;
759 rt
->m_on
[6] += 0x00000001;
760 rt
->m_on
[7] += 0x00000001;
766 rt
->m_dn
= LinkBox(c
->m_x
+ 8, c
->m_y
+ 8);
769 rt
->m_on
[6] += 0x00000001;
770 rt
->m_on
[7] += 0x00000001;
771 rt
->m_dn
->m_on
[0] += 0x00000001;
777 for (i
= 1; i
<= 3; i
++)
779 t1
= ((c
->m_live1
) >> (i
* 8)) & 0x000000ff;
782 c
->m_on
[i
- 1] += g_tab1
[t1
];
783 c
->m_on
[i
] += g_tab2
[t1
];
784 c
->m_on
[i
+ 1] += g_tab1
[t1
];
787 for (i
= 0; i
<= 2; i
++)
789 t1
= ((c
->m_live2
) >> (i
* 8)) & 0x000000ff;
792 c
->m_on
[i
+ 3] += g_tab1
[t1
];
793 c
->m_on
[i
+ 4] += g_tab2
[t1
];
794 c
->m_on
[i
+ 5] += g_tab1
[t1
];
819 t1
|= g_tab
[ ((t4
& 0x0000ffff) << 4 ) + ((t3
) & 0xf) ];
820 t1
|= g_tab
[ ((t4
& 0xffff0000) >> 12) + ((t3
>> 4 ) & 0xf) ] << 4;
822 t1
|= g_tab
[ ((t4
& 0x0000ffff) << 4 ) + ((t3
>> 8 ) & 0xf) ] << 8;
823 t1
|= g_tab
[ ((t4
& 0xffff0000) >> 12) + ((t3
>> 12) & 0xf) ] << 12;
825 t1
|= g_tab
[ ((t4
& 0x0000ffff) << 4 ) + ((t3
>> 16) & 0xf) ] << 16;
826 t1
|= g_tab
[ ((t4
& 0xffff0000) >> 12) + ((t3
>> 20) & 0xf) ] << 20;
828 t1
|= g_tab
[ ((t4
& 0x0000ffff) << 4 ) + ((t3
>> 24) & 0xf) ] << 24;
829 t1
|= g_tab
[ ((t4
& 0xffff0000) >> 12) + ((t3
>> 28) & 0xf) ] << 28;
835 t2
|= g_tab
[ ((t4
& 0x0000ffff) << 4 ) + ((t3
) & 0xf) ];
836 t2
|= g_tab
[ ((t4
& 0xffff0000) >> 12) + ((t3
>> 4 ) & 0xf) ] << 4;
838 t2
|= g_tab
[ ((t4
& 0x0000ffff) << 4 ) + ((t3
>> 8 ) & 0xf) ] << 8;
839 t2
|= g_tab
[ ((t4
& 0xffff0000) >> 12) + ((t3
>> 12) & 0xf) ] << 12;
841 t2
|= g_tab
[ ((t4
& 0x0000ffff) << 4 ) + ((t3
>> 16) & 0xf) ] << 16;
842 t2
|= g_tab
[ ((t4
& 0xffff0000) >> 12) + ((t3
>> 20) & 0xf) ] << 20;
844 t2
|= g_tab
[ ((t4
& 0x0000ffff) << 4 ) + ((t3
>> 24) & 0xf) ] << 24;
845 t2
|= g_tab
[ ((t4
& 0xffff0000) >> 12) + ((t3
>> 28) & 0xf) ] << 28;
847 c
->m_on
[0] = c
->m_on
[1] = c
->m_on
[2] = c
->m_on
[3] =
848 c
->m_on
[4] = c
->m_on
[5] = c
->m_on
[6] = c
->m_on
[7] = 0;
852 // count alive cells (TODO: find a better way to do this)
853 for (int i
= 0; i
< 32; i
++)
855 if (t1
& (1 << i
)) m_numcells
++;
856 if (t2
& (1 << i
)) m_numcells
++;
859 changed
|= ((t1
^ c
->m_old1
) || (t2
^ c
->m_old2
));
861 // mark, and discard if necessary, dead boxes
869 CellBox
*aux
= c
->m_next
;
870 if (c
->m_dead
++ > MAXDEAD
)
880 // ==========================================================================
882 // ==========================================================================
884 // A module to pregenerate lookup tables without having to do it
885 // from the application.
887 class LifeModule
: public wxModule
889 DECLARE_DYNAMIC_CLASS(LifeModule
)
897 IMPLEMENT_DYNAMIC_CLASS(LifeModule
, wxModule
)
899 bool LifeModule::OnInit()
902 g_tab
= new unsigned char [0xfffff];
904 if (!g_tab
) return FALSE
;
906 for (wxUint32 i
= 0; i
< 0xfffff; i
++)
908 wxUint32 val
= i
>> 4;
909 wxUint32 old
= i
& 0x0000f;
912 for (int j
= 0; j
< 4; j
++)
916 if (((val
& 0xf) == 3) || (((val
& 0xf) == 2) && (old
& 0x1)))
923 g_tab
[i
] = (unsigned char) live
;
929 void LifeModule::OnExit()
935 // This table converts from number of neighbors (like in on[]) to
936 // bits, for a set of four cells. It takes as index a five-digit
937 // hexadecimal value (0xNNNNB) where Ns hold number of neighbors
938 // for each cell and B holds their previous state.
940 unsigned char *g_tab
;
942 // This table converts from bits (like in live1, live2) to number
943 // of neighbors for each cell in the upper or lower row.
1205 // This table converts from bits (like in live1, live2) to number
1206 // of neighbors for each cell in the same row (excluding ourselves)