]>
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>
7 // Copyright: (c) 2000, Guillermo Rodriguez Garcia
8 // Licence: wxWindows licence
9 /////////////////////////////////////////////////////////////////////////////
11 // ==========================================================================
12 // headers, declarations, constants
13 // ==========================================================================
15 // For compilers that support precompilation, includes "wx/wx.h".
16 #include "wx/wxprec.h"
27 #include "wx/module.h"
30 #include <string.h> // for memset
33 #define CELLSARRAYSIZE 1024 // static array for BeginFind & co.
34 #define ALLOCBOXES 16 // number of cellboxes to alloc at once
35 #define MAXDEAD 8 // tics before removing cellbox from list
38 // ==========================================================================
40 // ==========================================================================
42 #define HASH(x, y) (((x >> 3) & 0x7f) << 7) + ((y >> 3) & 0x7f)
44 #define HASHSIZE 16384 // hash table size (do not change!)
45 #define CELLBOX 8 // cells in a cellbox (do not change!)
52 inline bool IsAlive(int dx
, int dy
) const;
53 inline bool SetCell(int dx
, int dy
, bool alive
);
56 wxInt32 m_x
, m_y
; // position in universe
57 wxUint32 m_live1
, m_live2
; // alive cells (1 bit per cell)
58 wxUint32 m_old1
, m_old2
; // old values for m_live1, 2
59 wxUint32 m_on
[8]; // neighbouring info
60 wxUint32 m_dead
; // been dead for n generations
61 LifeCellBox
*m_up
, *m_dn
, *m_lf
, *m_rt
; // neighbour CellBoxes
62 LifeCellBox
*m_prev
, *m_next
; // in linked list
63 LifeCellBox
*m_hprev
, *m_hnext
; // in hash table
68 // Returns whether cell dx, dy in this box is alive
70 bool LifeCellBox::IsAlive(int dx
, int dy
) const
73 return (m_live2
& 1 << ((dy
- 4) * 8 + dx
)) ? true : false ;
75 return (m_live1
& 1 << ((dy
) * 8 + dx
)) ? true : false ;
79 // Sets cell dx, dy in this box to 'alive', returns true if
80 // the previous value was different, false if it was the same.
82 bool LifeCellBox::SetCell(int dx
, int dy
, bool alive
)
84 if (IsAlive(dx
, dy
) != alive
)
87 m_live2
^= 1 << ((dy
- 4) * 8 + dx
);
89 m_live1
^= 1 << ((dy
) * 8 + dx
);
91 // reset this here to avoid updating problems
101 // ==========================================================================
103 // ==========================================================================
105 // --------------------------------------------------------------------------
107 // --------------------------------------------------------------------------
111 // pattern description
112 m_name
= wxEmptyString
;
113 m_rules
= wxEmptyString
;
114 m_description
= wxEmptyString
;
118 m_boxes
= new LifeCellBox
*[HASHSIZE
];
121 for (int i
= 0; i
< HASHSIZE
; i
++)
124 // state vars for BeginFind & FindMore
125 m_cells
= new LifeCell
[CELLSARRAYSIZE
];
140 // Clears the board, freeing all storage.
146 // clear the hash table pointers
147 for (int i
= 0; i
< HASHSIZE
; i
++)
160 // free available boxes
171 m_name
= wxEmptyString
;
172 m_rules
= wxEmptyString
;
173 m_description
= wxEmptyString
;
177 // --------------------------------------------------------------------------
178 // Test and set individual cells
179 // --------------------------------------------------------------------------
182 // Returns whether cell (x, y) is alive.
184 bool Life::IsAlive(wxInt32 x
, wxInt32 y
)
186 LifeCellBox
*c
= LinkBox(x
, y
, false);
188 return (c
&& c
->IsAlive( x
- c
->m_x
, y
- c
->m_y
));
192 // Sets or clears cell (x, y), according to the 'alive' param.
194 void Life::SetCell(wxInt32 x
, wxInt32 y
, bool alive
)
196 LifeCellBox
*c
= LinkBox(x
, y
);
197 wxUint32 dx
= x
- c
->m_x
;
198 wxUint32 dy
= y
- c
->m_y
;
200 if (c
->SetCell(dx
, dy
, alive
))
209 void Life::SetPattern(const LifePattern
& pattern
)
211 wxArrayString data
= pattern
.m_shape
;
217 for (size_t n
= 0; n
< data
.GetCount(); n
++)
221 if ( (line
.GetChar(0) != wxT('*')) &&
222 (line
.GetChar(0) != wxT('.')) )
224 // assume that it is a digit or a minus sign
225 line
.BeforeFirst(wxT(' ')).ToLong(&x
);
226 line
.AfterFirst(wxT(' ')).ToLong(&y
);
231 for (size_t k
= 0; k
< line
.Len(); k
++)
232 SetCell(x
+ k
, y
, line
.GetChar(k
) == wxT('*'));
238 m_name
= pattern
.m_name
;
239 m_rules
= pattern
.m_rules
;
240 m_description
= pattern
.m_description
;
243 // --------------------------------------------------------------------------
244 // Cellbox management functions
245 // --------------------------------------------------------------------------
248 // Creates a box in x, y, either taking it from the list
249 // of available boxes, or allocating a new one.
251 LifeCellBox
* Life::CreateBox(wxInt32 x
, wxInt32 y
, wxUint32 hv
)
255 // if there are no available boxes, alloc a few more
257 for (int i
= 1; i
<= ALLOCBOXES
; i
++)
259 c
= new LifeCellBox();
263 // TODO: handle memory errors. Note that right now, if we
264 // couldn't allocate at least one cellbox, we will crash
265 // before leaving CreateBox(). Probably we should try to
266 // allocate some boxes *before* the m_available list goes
267 // empty, so that we have a margin to handle errors
269 wxLogFatalError(_("Out of memory! Aborting..."));
274 c
->m_next
= m_available
;
278 // take a cellbox from the list of available boxes
280 m_available
= c
->m_next
;
283 memset((void *) c
, 0, sizeof(LifeCellBox
));
287 // insert c in the list
290 if (c
->m_next
) c
->m_next
->m_prev
= c
;
292 // insert c in the hash table
293 c
->m_hnext
= m_boxes
[hv
];
295 if (c
->m_hnext
) c
->m_hnext
->m_hprev
= c
;
301 // Returns a pointer to the box (x, y); if it didn't exist yet,
302 // it returns NULL or creates a new one, depending on the value
303 // of the 'create' parameter.
305 LifeCellBox
* Life::LinkBox(wxInt32 x
, wxInt32 y
, bool create
)
314 // search in the hash table
315 for (c
= m_boxes
[hv
]; c
; c
= c
->m_hnext
)
316 if ((c
->m_x
== x
) && (c
->m_y
== y
)) return c
;
318 // if not found, and (create == true), create a new one
319 return create
? CreateBox(x
, y
, hv
) : (LifeCellBox
*) NULL
;
323 // Removes this box from the list and the hash table and
324 // puts it in the list of available boxes.
326 void Life::KillBox(LifeCellBox
*c
)
328 wxUint32 hv
= HASH(c
->m_x
, c
->m_y
);
330 // remove from the list
332 c
->m_prev
->m_next
= c
->m_next
;
336 // remove from the hash table
337 if (c
!= m_boxes
[hv
])
338 c
->m_hprev
->m_hnext
= c
->m_hnext
;
340 m_boxes
[hv
] = c
->m_hnext
;
343 if (c
->m_next
) c
->m_next
->m_prev
= c
->m_prev
;
344 if (c
->m_hnext
) c
->m_hnext
->m_hprev
= c
->m_hprev
;
345 if (c
->m_up
) c
->m_up
->m_dn
= NULL
;
346 if (c
->m_dn
) c
->m_dn
->m_up
= NULL
;
347 if (c
->m_lf
) c
->m_lf
->m_rt
= NULL
;
348 if (c
->m_rt
) c
->m_rt
->m_lf
= NULL
;
350 // append to the list of available boxes
351 c
->m_next
= m_available
;
355 // --------------------------------------------------------------------------
357 // --------------------------------------------------------------------------
359 LifeCell
Life::FindCenter()
368 for (c
= m_head
; c
; c
= c
->m_next
)
378 sx
= (sx
/ n
) + CELLBOX
/ 2;
379 sy
= (sy
/ n
) + CELLBOX
/ 2;
383 cell
.i
= (wxInt32
) sx
;
384 cell
.j
= (wxInt32
) sy
;
388 LifeCell
Life::FindNorth()
390 wxInt32 x
= 0, y
= 0;
394 for (c
= m_head
; c
; c
= c
->m_next
)
395 if (!c
->m_dead
&& ((first
) || (c
->m_y
< y
)))
403 cell
.i
= first
? 0 : x
+ CELLBOX
/ 2;
404 cell
.j
= first
? 0 : y
+ CELLBOX
/ 2;
408 LifeCell
Life::FindSouth()
410 wxInt32 x
= 0, y
= 0;
414 for (c
= m_head
; c
; c
= c
->m_next
)
415 if (!c
->m_dead
&& ((first
) || (c
->m_y
> y
)))
423 cell
.i
= first
? 0 : x
+ CELLBOX
/ 2;
424 cell
.j
= first
? 0 : y
+ CELLBOX
/ 2;
428 LifeCell
Life::FindWest()
430 wxInt32 x
= 0, y
= 0;
434 for (c
= m_head
; c
; c
= c
->m_next
)
435 if (!c
->m_dead
&& ((first
) || (c
->m_x
< x
)))
443 cell
.i
= first
? 0 : x
+ CELLBOX
/ 2;
444 cell
.j
= first
? 0 : y
+ CELLBOX
/ 2;
448 LifeCell
Life::FindEast()
450 wxInt32 x
= 0, y
= 0;
454 for (c
= m_head
; c
; c
= c
->m_next
)
455 if (!c
->m_dead
&& ((first
) || (c
->m_x
> x
)))
463 cell
.i
= first
? 0 : x
+ CELLBOX
/ 2;
464 cell
.j
= first
? 0 : y
+ CELLBOX
/ 2;
468 // --------------------------------------------------------------------------
470 // --------------------------------------------------------------------------
473 // Post eight cells to the cell arrays - leave out the fourth
474 // argument (or pass 0, the default value) to post alive cells
475 // only, else it will post cells which have changed.
477 void Life::DoLine(wxInt32 x
, wxInt32 y
, wxUint32 live
, wxUint32 old
)
479 wxUint32 diff
= (live
^ old
) & 0xff;
483 for (wxInt32 k
= 8; k
; k
--, x
++)
487 m_cells
[m_ncells
].i
= x
;
488 m_cells
[m_ncells
].j
= y
;
495 void Life::BeginFind(wxInt32 x0
, wxInt32 y0
, wxInt32 x1
, wxInt32 y1
, bool changed
)
497 // TODO: optimize for the case where the maximum number of
498 // cellboxes that fit in the specified viewport is smaller
499 // than the current total of boxes; iterating over the list
500 // should then be faster than searching in the hash table.
502 m_x0
= m_x
= x0
& 0xfffffff8;
503 m_y0
= m_y
= y0
& 0xfffffff8;
504 m_x1
= (x1
+ 7) & 0xfffffff8;
505 m_y1
= (y1
+ 7) & 0xfffffff8;
511 bool Life::FindMore(LifeCell
*cells
[], size_t *ncells
)
519 for ( ; m_y
<= m_y1
; m_y
+= 8, m_x
= m_x0
)
520 for ( ; m_x
<= m_x1
; m_x
+= 8)
522 if ((c
= LinkBox(m_x
, m_y
, false)) == NULL
)
525 // check whether there is enough space left in the array
526 if (m_ncells
> (CELLSARRAYSIZE
- 64))
532 DoLine(m_x
, m_y
, c
->m_live1
, c
->m_old1
);
533 DoLine(m_x
, m_y
+ 1, c
->m_live1
>> 8, c
->m_old1
>> 8 );
534 DoLine(m_x
, m_y
+ 2, c
->m_live1
>> 16, c
->m_old1
>> 16);
535 DoLine(m_x
, m_y
+ 3, c
->m_live1
>> 24, c
->m_old1
>> 24);
536 DoLine(m_x
, m_y
+ 4, c
->m_live2
, c
->m_old2
);
537 DoLine(m_x
, m_y
+ 5, c
->m_live2
>> 8, c
->m_old2
>> 8 );
538 DoLine(m_x
, m_y
+ 6, c
->m_live2
>> 16, c
->m_old2
>> 16);
539 DoLine(m_x
, m_y
+ 7, c
->m_live2
>> 24, c
->m_old2
>> 24);
544 for ( ; m_y
<= m_y1
; m_y
+= 8, m_x
= m_x0
)
545 for ( ; m_x
<= m_x1
; m_x
+= 8)
547 if ((c
= LinkBox(m_x
, m_y
, false)) == NULL
)
550 // check whether there is enough space left in the array
551 if (m_ncells
> (CELLSARRAYSIZE
- 64))
557 DoLine(m_x
, m_y
, c
->m_live1
);
558 DoLine(m_x
, m_y
+ 1, c
->m_live1
>> 8 );
559 DoLine(m_x
, m_y
+ 2, c
->m_live1
>> 16);
560 DoLine(m_x
, m_y
+ 3, c
->m_live1
>> 24);
561 DoLine(m_x
, m_y
+ 4, c
->m_live2
);
562 DoLine(m_x
, m_y
+ 5, c
->m_live2
>> 8 );
563 DoLine(m_x
, m_y
+ 6, c
->m_live2
>> 16);
564 DoLine(m_x
, m_y
+ 7, c
->m_live2
>> 24);
573 // --------------------------------------------------------------------------
575 // --------------------------------------------------------------------------
577 extern unsigned char *g_tab
;
582 // Advance one step in evolution :-)
586 LifeCellBox
*c
, *up
, *dn
, *lf
, *rt
;
587 wxUint32 t1
, t2
, t3
, t4
;
588 bool changed
= false;
593 // Compute neighbours of each cell
595 // WARNING: unrolled loops and lengthy code follows!
601 if (! (c
->m_live1
|| c
->m_live2
))
612 t1
= c
->m_live1
& 0x000000ff;
617 up
= LinkBox(c
->m_x
, c
->m_y
- 8);
623 c
->m_on
[0] += g_tab2
[t1
];
627 t1
= (c
->m_live2
& 0xff000000) >> 24;
632 dn
= LinkBox(c
->m_x
, c
->m_y
+ 8);
638 c
->m_on
[7] += g_tab2
[t1
];
649 lf
= LinkBox(c
->m_x
- 8, c
->m_y
);
656 lf
->m_up
= LinkBox(c
->m_x
- 8, c
->m_y
- 8);
659 lf
->m_up
->m_on
[7] += 0x10000000;
660 lf
->m_on
[0] += 0x10000000;
661 lf
->m_on
[1] += 0x10000000;
665 lf
->m_on
[0] += 0x10000000;
666 lf
->m_on
[1] += 0x10000000;
667 lf
->m_on
[2] += 0x10000000;
671 lf
->m_on
[1] += 0x10000000;
672 lf
->m_on
[2] += 0x10000000;
673 lf
->m_on
[3] += 0x10000000;
677 lf
->m_on
[2] += 0x10000000;
678 lf
->m_on
[3] += 0x10000000;
679 lf
->m_on
[4] += 0x10000000;
686 lf
= LinkBox(c
->m_x
- 8, c
->m_y
);
691 lf
->m_on
[3] += 0x10000000;
692 lf
->m_on
[4] += 0x10000000;
693 lf
->m_on
[5] += 0x10000000;
697 lf
->m_on
[4] += 0x10000000;
698 lf
->m_on
[5] += 0x10000000;
699 lf
->m_on
[6] += 0x10000000;
703 lf
->m_on
[5] += 0x10000000;
704 lf
->m_on
[6] += 0x10000000;
705 lf
->m_on
[7] += 0x10000000;
711 lf
->m_dn
= LinkBox(c
->m_x
- 8, c
->m_y
+ 8);
714 lf
->m_on
[6] += 0x10000000;
715 lf
->m_on
[7] += 0x10000000;
716 lf
->m_dn
->m_on
[0] += 0x10000000;
725 rt
= LinkBox(c
->m_x
+ 8, c
->m_y
);
732 rt
->m_up
= LinkBox(c
->m_x
+ 8, c
->m_y
- 8);
735 rt
->m_up
->m_on
[7] += 0x00000001;
736 rt
->m_on
[0] += 0x00000001;
737 rt
->m_on
[1] += 0x00000001;
741 rt
->m_on
[0] += 0x00000001;
742 rt
->m_on
[1] += 0x00000001;
743 rt
->m_on
[2] += 0x00000001;
747 rt
->m_on
[1] += 0x00000001;
748 rt
->m_on
[2] += 0x00000001;
749 rt
->m_on
[3] += 0x00000001;
753 rt
->m_on
[2] += 0x00000001;
754 rt
->m_on
[3] += 0x00000001;
755 rt
->m_on
[4] += 0x00000001;
762 rt
= LinkBox(c
->m_x
+ 8, c
->m_y
);
767 rt
->m_on
[3] += 0x00000001;
768 rt
->m_on
[4] += 0x00000001;
769 rt
->m_on
[5] += 0x00000001;
773 rt
->m_on
[4] += 0x00000001;
774 rt
->m_on
[5] += 0x00000001;
775 rt
->m_on
[6] += 0x00000001;
779 rt
->m_on
[5] += 0x00000001;
780 rt
->m_on
[6] += 0x00000001;
781 rt
->m_on
[7] += 0x00000001;
787 rt
->m_dn
= LinkBox(c
->m_x
+ 8, c
->m_y
+ 8);
790 rt
->m_on
[6] += 0x00000001;
791 rt
->m_on
[7] += 0x00000001;
792 rt
->m_dn
->m_on
[0] += 0x00000001;
798 for (i
= 1; i
<= 3; i
++)
800 t1
= ((c
->m_live1
) >> (i
* 8)) & 0x000000ff;
803 c
->m_on
[i
- 1] += g_tab1
[t1
];
804 c
->m_on
[i
] += g_tab2
[t1
];
805 c
->m_on
[i
+ 1] += g_tab1
[t1
];
808 for (i
= 0; i
<= 2; i
++)
810 t1
= ((c
->m_live2
) >> (i
* 8)) & 0x000000ff;
813 c
->m_on
[i
+ 3] += g_tab1
[t1
];
814 c
->m_on
[i
+ 4] += g_tab2
[t1
];
815 c
->m_on
[i
+ 5] += g_tab1
[t1
];
840 t1
|= g_tab
[ ((t4
& 0x0000ffff) << 4 ) + ((t3
) & 0xf) ];
841 t1
|= g_tab
[ ((t4
& 0xffff0000) >> 12) + ((t3
>> 4 ) & 0xf) ] << 4;
843 t1
|= g_tab
[ ((t4
& 0x0000ffff) << 4 ) + ((t3
>> 8 ) & 0xf) ] << 8;
844 t1
|= g_tab
[ ((t4
& 0xffff0000) >> 12) + ((t3
>> 12) & 0xf) ] << 12;
846 t1
|= g_tab
[ ((t4
& 0x0000ffff) << 4 ) + ((t3
>> 16) & 0xf) ] << 16;
847 t1
|= g_tab
[ ((t4
& 0xffff0000) >> 12) + ((t3
>> 20) & 0xf) ] << 20;
849 t1
|= g_tab
[ ((t4
& 0x0000ffff) << 4 ) + ((t3
>> 24) & 0xf) ] << 24;
850 t1
|= g_tab
[ ((t4
& 0xffff0000) >> 12) + ((t3
>> 28) & 0xf) ] << 28;
856 t2
|= g_tab
[ ((t4
& 0x0000ffff) << 4 ) + ((t3
) & 0xf) ];
857 t2
|= g_tab
[ ((t4
& 0xffff0000) >> 12) + ((t3
>> 4 ) & 0xf) ] << 4;
859 t2
|= g_tab
[ ((t4
& 0x0000ffff) << 4 ) + ((t3
>> 8 ) & 0xf) ] << 8;
860 t2
|= g_tab
[ ((t4
& 0xffff0000) >> 12) + ((t3
>> 12) & 0xf) ] << 12;
862 t2
|= g_tab
[ ((t4
& 0x0000ffff) << 4 ) + ((t3
>> 16) & 0xf) ] << 16;
863 t2
|= g_tab
[ ((t4
& 0xffff0000) >> 12) + ((t3
>> 20) & 0xf) ] << 20;
865 t2
|= g_tab
[ ((t4
& 0x0000ffff) << 4 ) + ((t3
>> 24) & 0xf) ] << 24;
866 t2
|= g_tab
[ ((t4
& 0xffff0000) >> 12) + ((t3
>> 28) & 0xf) ] << 28;
868 c
->m_on
[0] = c
->m_on
[1] = c
->m_on
[2] = c
->m_on
[3] =
869 c
->m_on
[4] = c
->m_on
[5] = c
->m_on
[6] = c
->m_on
[7] = 0;
877 t1_
= (t1
& 0x55555555) + (t1
>> 1 & 0x55555555);
878 t1_
= (t1_
& 0x33333333) + (t1_
>> 2 & 0x33333333);
880 t2_
= (t2
& 0x55555555) + (t2
>> 1 & 0x55555555);
881 t2_
= (t2_
& 0x33333333) + (t2_
>> 2 & 0x33333333) + t1_
;
882 t2_
= (t2_
& 0x0F0F0F0F) + (t2_
>> 4 & 0x0F0F0F0F);
883 t2_
= (t2_
& 0x00FF00FF) + (t2_
>> 8 & 0x00FF00FF);
885 m_numcells
+= (t2_
& 0xFF) + (t2_
>> 16 & 0xFF);
887 // Original, slower code
888 for (int i
= 0; i
< 32; i
++)
890 if (t1
& (1 << i
)) m_numcells
++;
891 if (t2
& (1 << i
)) m_numcells
++;
895 changed
|= ((t1
^ c
->m_old1
) || (t2
^ c
->m_old2
));
897 // mark, and discard if necessary, dead boxes
905 LifeCellBox
*aux
= c
->m_next
;
906 if (c
->m_dead
++ > MAXDEAD
)
916 // ==========================================================================
918 // ==========================================================================
920 // A module to pregenerate lookup tables without having to do it
921 // from the application.
923 class LifeModule
: public wxModule
925 DECLARE_DYNAMIC_CLASS(LifeModule
)
933 IMPLEMENT_DYNAMIC_CLASS(LifeModule
, wxModule
)
935 bool LifeModule::OnInit()
938 g_tab
= new unsigned char [0xfffff];
940 if (!g_tab
) return false;
942 for (wxUint32 i
= 0; i
< 0xfffff; i
++)
944 wxUint32 val
= i
>> 4;
945 wxUint32 old
= i
& 0x0000f;
948 for (int j
= 0; j
< 4; j
++)
952 if (((val
& 0xf) == 3) || (((val
& 0xf) == 2) && (old
& 0x1)))
959 g_tab
[i
] = (unsigned char) live
;
965 void LifeModule::OnExit()
971 // This table converts from number of neighbors (like in on[]) to
972 // bits, for a set of four cells. It takes as index a five-digit
973 // hexadecimal value (0xNNNNB) where Ns hold number of neighbors
974 // for each cell and B holds their previous state.
976 unsigned char *g_tab
;
978 // This table converts from bits (like in live1, live2) to number
979 // of neighbors for each cell in the upper or lower row.
1241 // This table converts from bits (like in live1, live2) to number
1242 // of neighbors for each cell in the same row (excluding ourselves)