abrash-black-book/17-02.html
2013-12-30 12:21:49 +11:00

324 lines
9.7 KiB
HTML

<HTML>
<HEAD>
<META name=vsisbn content="1576101746">
<META name=vstitle content="Michael Abrash's Graphics Programming Black Book, Special Edition">
<META name=vsauthor content="Michael Abrash">
<META name=vspublisher content="The Coriolis Group">
<META name=vspubdate content="07/01/97">
<META name=vscategory content="Web and Software Development: Game Development,Web and Software Development: Graphics and Multimedia Development">
<TITLE>Michael Abrash's Graphics Programming Black Book Special Edition: The Game of Life</TITLE>
<!-- HEADER -->
<!-- Empty Reference Subhead -->
<!--ISBN=1576101746//-->
<!--TITLE=Michael Abrash's Graphics Programming Black Book Special Edition//-->
<!--AUTHOR=Michael Abrash//-->
<!--PUBLISHER=The Coriolis Group, Inc.//-->
<!--CHAPTER=17//-->
<!--PAGES=325-329//-->
<!--UNASSIGNED1//-->
<!--UNASSIGNED2//--></HEAD><BODY LINK=#0000FF ALINK=#000099 VLINK=#0000FF BGCOLOR=#FFFFFF>
<CENTER>
<TABLE BORDER>
<TR>
<TD><A HREF="17-01.html">Previous</A></TD>
<TD><A HREF="index.html">Table of Contents</A></TD>
<TD><A HREF="17-03.html">Next</A></TD>
</TR>
</TABLE>
</CENTER>
<P><BR></P>
<P><B>LISTING 17.1 L17-1.CPP</B></P>
<!-- CODE //-->
<PRE>
/* C&#43;&#43; Game of Life implementation for any mode for which mode set
and draw pixel functions can be provided.
Tested with Borland C&#43;&#43; in the small model. */
#include &ltstdlib.h&gt
#include &ltstdio.h&gt
#include &ltiostream.h&gt
#include &ltconio.h&gt
#include &lttime.h&gt
#include &ltdos.h&gt
#include &ltbios.h&gt
#include &ltmem.h&gt
#define ON_COLOR 15 // on-cell pixel color
#define OFF_COLOR 0 // off-cell pixel color
#define MSG_LINE 10 // row for text messages
#define GENERATION_LINE 12 // row for generation # display
#define LIMIT_18_HZ 1 // set 1 for maximum frame rate = 18Hz
#define WRAP_EDGES 1 // set to 0 to disable wrapping around
// at cell map edges
class cellmap {
private:
unsigned char *cells;
unsigned int width;
unsigned int width_in_bytes;
unsigned int height;
unsigned int length_in_bytes;
public:
cellmap(unsigned int h, unsigned int v);
~cellmap(void);
void copy_cells(cellmap &ampsourcemap);
void set_cell(unsigned int x, unsigned int y);
void clear_cell(unsigned int x, unsigned int y);
int cell_state(int x, int y);
void next_generation(cellmap&amp dest_map);
};
extern void enter_display_mode(void);
extern void exit_display_mode(void);
extern void draw_pixel(unsigned int X, unsigned int Y,
unsigned int Color);
extern void show_text(int x, int y, char *text);
/* Controls the size of the cell map. Must be within the capabilities
of the display mode, and must be limited to leave room for text
display at right. */
unsigned int cellmap_width = 96;
unsigned int cellmap_height = 96;
/* Width &amp height in pixels of each cell as displayed on screen. */
unsigned int magnifier = 2;
void main()
{
unsigned int init_length, x, y, seed;
unsigned long generation = 0;
char gen_text[80];
long bios_time, start_bios_time;
cellmap current_map(cellmap_height, cellmap_width);
cellmap next_map(cellmap_height, cellmap_width);
// Get the seed; seed randomly if 0 entered
cout &lt&lt &#147;Seed (0 for random seed): &#148;;
cin &gt&gt seed;
if (seed == 0) seed = (unsigned) time(NULL);
// Randomly initialize the initial cell map
cout &lt&lt &#147;Initializing...&#148;;
srand(seed);
init_length = (cellmap_height * cellmap_width) / 2;
do {
x = random(cellmap_width);
y = random(cellmap_height);
next_map.set_cell(x, y);
} while (&#151;init_length);
current_map.copy_cells(next_map); // put init map in current_map
enter_display_mode();
// Keep recalculating and redisplaying generations until a key
// is pressed
show_text(0, MSG_LINE, &#147;Generation: &#148;);
start_bios_time = _bios_timeofday(_TIME_GETCLOCK, &ampbios_time);
do {
generation&#43;&#43;;
sprintf(gen_text, &#147;%10lu&#148;, generation);
show_text(1, GENERATION_LINE, gen_text);
// Recalculate and draw the next generation
current_map.next_generation(next_map);
// Make current_map current again
current_map.copy_cells(next_map);
#if LIMIT_18_HZ
// Limit to a maximum of 18.2 frames per second,for visibility
do {
_bios_timeofday(_TIME_GETCLOCK, &ampbios_time);
} while (start_bios_time == bios_time);
start_bios_time = bios_time;
#endif
} while (!kbhit());
getch(); // clear keypress
exit_display_mode();
cout &lt&lt &#147;Total generations: &#148; &lt&lt generation &lt&lt &#147;\nSeed: &#148; &lt&lt
seed &lt&lt &#147;\n&#148;;
}
/* cellmap constructor. */
cellmap::cellmap(unsigned int h, unsigned int w)
{
width = w;
width_in_bytes = (w &#43; 7) / 8;
height = h;
length_in_bytes = width_in_bytes * h;
cells = new unsigned char[length_in_bytes]; // cell storage
memset(cells, 0, length_in_bytes); // clear all cells, to start
}
/* cellmap destructor. */
cellmap::~cellmap(void)
{
delete[] cells;
}
/* Copies one cellmap&#146;s cells to another cellmap. Both cellmaps are
assumed to be the same size. */
void cellmap::copy_cells(cellmap &ampsourcemap)
{
memcpy(cells, sourcemap.cells, length_in_bytes);
}
/* Turns cell on. */
void cellmap::set_cell(unsigned int x, unsigned int y)
{
unsigned char *cell_ptr =
cells &#43; (y * width_in_bytes) &#43; (x / 8);
*(cell_ptr) |= 0x80 &gt&gt (x &amp 0x07);
}
/* Turns cell off. */
void cellmap::clear_cell(unsigned int x, unsigned int y)
{
unsigned char *cell_ptr =
cells &#43; (y * width_in_bytes) &#43; (x / 8);
*(cell_ptr) &amp= ~(0x80 &gt&gt (x &amp 0x07));
}
/* Returns cell state (1=on or 0=off), optionally wrapping at the
borders around to the opposite edge. */
int cellmap::cell_state(int x, int y)
{
unsigned char *cell_ptr;
#if WRAP_EDGES
while (x &lt 0) x &#43;= width; // wrap, if necessary
while (x &gt= width) x -= width;
while (y &lt 0) y &#43;= height;
while (y &gt= height) y -= height;
#else
if ((x &lt 0) || (x &gt= width) || (y &lt 0) || (y &gt= height))
return 0; // return 0 for off edges if no wrapping
#endif
cell_ptr = cells &#43; (y * width_in_bytes) &#43; (x / 8);
return (*cell_ptr &amp (0x80 &gt&gt (x &amp 0x07))) ? 1 : 0;
}
/* Calculates the next generation of a cellmap and stores it in
next_map. */
void cellmap::next_generation(cellmap&amp next_map)
{
unsigned int x, y, neighbor_count;
for (y=0; y&ltheight; y&#43;&#43;) {
for (x=0; x&ltwidth; x&#43;&#43;) {
// Figure out how many neighbors this cell has
neighbor_count = cell_state(x-1, y-1) &#43; cell_state(x, y-1) &#43;
cell_state(x&#43;1, y-1) &#43; cell_state(x-1, y) &#43;
cell_state(x&#43;1, y) &#43; cell_state(x-1, y&#43;1) &#43;
cell_state(x, y&#43;1) &#43; cell_state(x&#43;1, y&#43;1);
if (cell_state(x, y) == 1) {
// The cell is on; does it stay on?
if ((neighbor_count != 2) &amp&amp (neighbor_count != 3)) {
next_map.clear_cell(x, y); // turn it off
draw_pixel(x, y, OFF_COLOR);
}
} else {
// The cell is off; does it turn on?
if (neighbor_count == 3) {
next_map.set_cell(x, y); // turn it on
draw_pixel(x, y, ON_COLOR);
}
}
}
}
}
</PRE>
<!-- END CODE //-->
<P><B>LISTING 17.2 L17-2.CPP</B></P>
<!-- CODE //-->
<PRE>
/* VGA mode 13h functions for Game of Life.
Tested with Borland C&#43;&#43;. */
#include &ltstdio.h&gt
#include &ltconio.h&gt
#include &ltdos.h&gt
#define TEXT_X_OFFSET 27
#define SCREEN_WIDTH_IN_BYTES 320
/* Width &amp height in pixels of each cell. */
extern unsigned int magnifier;
/* Mode 13h draw pixel function. Pixels are of width &amp height
specified by magnifier. */
void draw_pixel(unsigned int x, unsigned int y, unsigned int color)
{
#define SCREEN_SEGMENT 0xA000
unsigned char far *screen_ptr;
int i, j;
FP_SEG(screen_ptr) = SCREEN_SEGMENT;
FP_OFF(screen_ptr) =
y * magnifier * SCREEN_WIDTH_IN_BYTES &#43; x * magnifier;
for (i=0; i&ltmagnifier; i&#43;&#43;) {
for (j=0; j&ltmagnifier; j&#43;&#43;) {
*(screen_ptr&#43;j) = color;
}
screen_ptr &#43;= SCREEN_WIDTH_IN_BYTES;
}
}
/* Mode 13h mode-set function. */
void enter_display_mode()
{
union REGS regset;
regset.x.ax = 0x0013;
int86(0x10, &ampregset, &ampregset);
}
/* Text mode mode-set function. */
void exit_display_mode()
{
union REGS regset;
regset.x.ax = 0x0003;
int86(0x10, &ampregset, &ampregset);
}
/* Text display function. Offsets text to non-graphics area of
screen. */
void show_text(int x, int y, char *text)
{
gotoxy(TEXT_X_OFFSET &#43; x, y);
puts(text);
}
</PRE>
<!-- END CODE //-->
<P><BR></P>
<CENTER>
<TABLE BORDER>
<TR>
<TD><A HREF="17-01.html">Previous</A></TD>
<TD><A HREF="index.html">Table of Contents</A></TD>
<TD><A HREF="17-03.html">Next</A></TD>
</TR>
</TABLE>
</CENTER>
<hr width="90%" size="1" noshade>
<div align="center">
<font face="Verdana,sans-serif" size="1">Graphics Programming Black Book &copy; 2001 Michael Abrash</font>
</div>
<!-- all of the reference materials (books) have the footer and subfoot reveresed -->
<!-- reference_subfoot = footer -->
<!-- reference_footer = subfoot -->
<!-- BEGIN SUB FOOTER -->
</BODY>
</HTML>
<!-- END FOOTER -->