abrash-black-book/17-02.md
2013-12-30 17:51:31 +11:00

267 lines
8.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

------------------------ --------------------------------- --------------------
[Previous](17-01.html) [Table of Contents](index.html) [Next](17-03.html)
------------------------ --------------------------------- --------------------
**LISTING 17.1 L17-1.CPP**
/* 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);
}
}
}
}
}
**LISTING 17.2 L17-2.CPP**
/* 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);
}
------------------------ --------------------------------- --------------------
[Previous](17-01.html) [Table of Contents](index.html) [Next](17-03.html)
------------------------ --------------------------------- --------------------
* * * * *
Graphics Programming Black Book © 2001 Michael Abrash