324 lines
9.7 KiB
HTML
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++ Game of Life implementation for any mode for which mode set
|
|
and draw pixel functions can be provided.
|
|
Tested with Borland C++ in the small model. */
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <iostream.h>
|
|
#include <conio.h>
|
|
#include <time.h>
|
|
#include <dos.h>
|
|
#include <bios.h>
|
|
#include <mem.h>
|
|
|
|
#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 &sourcemap);
|
|
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& 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 & 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 << “Seed (0 for random seed): ”;
|
|
cin >> seed;
|
|
if (seed == 0) seed = (unsigned) time(NULL);
|
|
|
|
// Randomly initialize the initial cell map
|
|
cout << “Initializing...”;
|
|
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 (—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, “Generation: ”);
|
|
start_bios_time = _bios_timeofday(_TIME_GETCLOCK, &bios_time);
|
|
do {
|
|
generation++;
|
|
sprintf(gen_text, “%10lu”, 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, &bios_time);
|
|
} while (start_bios_time == bios_time);
|
|
start_bios_time = bios_time;
|
|
#endif
|
|
} while (!kbhit());
|
|
getch(); // clear keypress
|
|
exit_display_mode();
|
|
cout << “Total generations: ” << generation << “\nSeed: ” <<
|
|
seed << “\n”;
|
|
}
|
|
|
|
/* cellmap constructor. */
|
|
cellmap::cellmap(unsigned int h, unsigned int w)
|
|
{
|
|
width = w;
|
|
width_in_bytes = (w + 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’s cells to another cellmap. Both cellmaps are
|
|
assumed to be the same size. */
|
|
void cellmap::copy_cells(cellmap &sourcemap)
|
|
{
|
|
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 + (y * width_in_bytes) + (x / 8);
|
|
|
|
*(cell_ptr) |= 0x80 >> (x & 0x07);
|
|
}
|
|
|
|
/* Turns cell off. */
|
|
void cellmap::clear_cell(unsigned int x, unsigned int y)
|
|
{
|
|
unsigned char *cell_ptr =
|
|
cells + (y * width_in_bytes) + (x / 8);
|
|
|
|
*(cell_ptr) &= ~(0x80 >> (x & 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 < 0) x += width; // wrap, if necessary
|
|
while (x >= width) x -= width;
|
|
while (y < 0) y += height;
|
|
while (y >= height) y -= height;
|
|
#else
|
|
if ((x < 0) || (x >= width) || (y < 0) || (y >= height))
|
|
return 0; // return 0 for off edges if no wrapping
|
|
#endif
|
|
cell_ptr = cells + (y * width_in_bytes) + (x / 8);
|
|
return (*cell_ptr & (0x80 >> (x & 0x07))) ? 1 : 0;
|
|
}
|
|
|
|
/* Calculates the next generation of a cellmap and stores it in
|
|
next_map. */
|
|
void cellmap::next_generation(cellmap& next_map)
|
|
{
|
|
unsigned int x, y, neighbor_count;
|
|
|
|
for (y=0; y<height; y++) {
|
|
for (x=0; x<width; x++) {
|
|
// Figure out how many neighbors this cell has
|
|
neighbor_count = cell_state(x-1, y-1) + cell_state(x, y-1) +
|
|
cell_state(x+1, y-1) + cell_state(x-1, y) +
|
|
cell_state(x+1, y) + cell_state(x-1, y+1) +
|
|
cell_state(x, y+1) + cell_state(x+1, y+1);
|
|
if (cell_state(x, y) == 1) {
|
|
// The cell is on; does it stay on?
|
|
if ((neighbor_count != 2) && (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++. */
|
|
#include <stdio.h>
|
|
#include <conio.h>
|
|
#include <dos.h>
|
|
|
|
#define TEXT_X_OFFSET 27
|
|
#define SCREEN_WIDTH_IN_BYTES 320
|
|
|
|
/* Width & height in pixels of each cell. */
|
|
extern unsigned int magnifier;
|
|
|
|
/* Mode 13h draw pixel function. Pixels are of width & 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 + x * magnifier;
|
|
for (i=0; i<magnifier; i++) {
|
|
for (j=0; j<magnifier; j++) {
|
|
*(screen_ptr+j) = color;
|
|
}
|
|
screen_ptr += SCREEN_WIDTH_IN_BYTES;
|
|
}
|
|
}
|
|
|
|
/* Mode 13h mode-set function. */
|
|
void enter_display_mode()
|
|
{
|
|
union REGS regset;
|
|
|
|
regset.x.ax = 0x0013;
|
|
int86(0x10, &regset, &regset);
|
|
}
|
|
|
|
/* Text mode mode-set function. */
|
|
void exit_display_mode()
|
|
{
|
|
union REGS regset;
|
|
|
|
regset.x.ax = 0x0003;
|
|
int86(0x10, &regset, &regset);
|
|
}
|
|
|
|
/* Text display function. Offsets text to non-graphics area of
|
|
screen. */
|
|
void show_text(int x, int y, char *text)
|
|
{
|
|
gotoxy(TEXT_X_OFFSET + 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 © 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 -->
|
|
|
|
|