home *** CD-ROM | disk | FTP | other *** search
- /* LIFE.C - Demonstrationsprogramm für das Spiel LIFE
- *
- * Sofern GRAPHICS.LIB und PGCHART.LIB bei der Installation nicht
- * in die Laufzeit-Bibliothek eingefügt wurden, müssen die beiden
- * Bibliotheken in die Programmliste von GRDEMO.MAK aufgenommen
- * oder auf der Befehlszeile angegeben werden.
- *
- * Das Spiel Life wurde 1970 vom Mathematiker John Horton Conway erfunden.
- * Ziel ist, Lebensformen zu schaffen und zu untersuchen, die sich aus Mustern
- * ergeben, die auf einem 'Spielbrett' eingegeben wurden (normalerweise der
- * Bildschirm, man kann es aber auch mit Papier und Bleistift spielen).
- *
- * Das Spiel Life basiert auf folgenden Gesetzen:
- *
- * 1. Das Gesetz des Überlebens - Wenn eine lebende Zelle entweder
- * zwei oder drei Nachbarzellen hat, überlebt sie.
- *
- * 2. Das Gesetz des Absterbens - Eine lebende Zelle mit mehr als drei
- * Nachbarn stirbt wegen 'Überbevölkerung', eine mit weniger als
- * zwei Nachbarzellen stirbt an 'Einsamkeit'.
- *
- * 3. Das Gesetz der Geburt - In der nächsten Generation wird eine
- * tote Zelle mit genau drei Nachbarzellen geboren.
- *
- * Aus diesen einfachen Gesetzen ergeben sich komplexe Gegenwirkungen.
- * Versuchen Sie beispielsweise einmal folgende Muster einzugeben:
- *
- * ■■ ■ ■ ■■ ■ ■
- * ■ ■ ■ ■ ■■ ■■■■■ ■■ ■■■■ ■■
- * ■ ■■ ■■■ ■ ■ ■ ■ ■ ■ ■
- * ■■■■
- */
-
- #include <string.h>
- #include <stdlib.h>
- #include <stdio.h>
- #include <conio.h>
- #include <ctype.h>
- #include <time.h>
- #include <graph.h>
- #include "tools.h"
-
- /* Dimensionen der Bevölkerungsmatrix für den größtmöglichen Bildschirm --
- * VGA 80 * 50).
- */
- #define MAXREIH 46
- #define MAXSPLT 78
- #define MAXBILD ((MAXREIH + 2) * (MAXSPLT + 2))
-
- #define BREIH (cfg.mzeile + 2) /* Bildschirm-Zeilen */
- #define BSPLT (cfg.msplt + 2) /* Bildschirm-Spalten */
- #define BBUFSIZE (BREIH * BSPLT) /* Größe Bildschirm-Zwspeicher */
- #define MBUFSIZE (cfg.mzeile * cfg.msplt) /* Größe Matrix-Zwischenspeicher */
-
- #define SIGNAL 7
-
- /* Aktion für status_zelle */
- enum ZELLENSTATUS { AUS, EIN, UMGESCH };
-
- /* Der Zellentyp bestimmt Symbol und Attribute einer Zelle.
- * Leben und Tod, die zwei häufigsten Zellen, werden initialisiert.
- */
- typedef struct ZELLENTYP
- {
- unsigned char symbol;
- unsigned char atrib;
- } ZELLE;
- ZELLE leben =
- { '■',
- SETATRIB( _TBRIGHTWHITE, _TBLACK )
- };
- ZELLE tod =
- { ' ',
- SETATRIB( _TBRIGHTWHITE, _TBLACK )
- };
-
- /* Struktur für Gesamtattribute von LIFE, für Mono initialisiert */
- struct LIFECONFIG
- {
- float dichte; /* Prozent Zufallsverteilung */
- int rescan; /* Abtastwiederholung für CGA */
- int far *videomem; /* Adresse des Videospeichers */
- char boxatrib; /* Attribut für Rahmen */
- char hlfatrib; /* Attribut für Dialogzeile */
- int mzeile; /* Matrix-Zeilen */
- int msplt; /* Matrix-Spalten */
- unsigned cursor; /* Anfangs- u. Endzeilen für Cursor */
- } cfg =
- {
- 0.40,
- FALSCH,
- (int far *)0xb0000000,
- SETATRIB( _TWHITE, _TBLACK ),
- SETATRIB( _TBLACK, _TWHITE ),
- 21,
- 78
- };
-
- /* Globalvariablen */
- char mat1[MAXREIH][MAXSPLT]; /* Matrix 1: speichert ggw. 'Bevölkerung' */
- char mat2[MAXREIH][MAXSPLT]; /* Matrix 2: speichert Überbevölkerungszahlen */
- int zelle; /* Zellenzeichen */
- char attrib; /* Videoattribut einer jeden Position */
- int endlos; /* Unbegrenzte Anzahl von Generationen? */
- long grenze; /* Höchstanzahl auszuführender Generationen */
- ZELLE bildpfr[MAXBILD]; /* Bildschirm-Zwischenspeicherbereich */
-
- /* Tastencodes */
- #define HM 0x0147
- #define UA 0x0148
- #define PU 0x0149
- #define RA 0x014d
- #define PD 0x0151
- #define DA 0x0150
- #define ED 0x014f
- #define LA 0x014b
- #define SH_HM 0x0247
- #define SH_UA 0x0248
- #define SH_PU 0x0249
- #define SH_RA 0x024d
- #define SH_PD 0x0251
- #define SH_DA 0x0250
- #define SH_ED 0x024f
- #define SH_LA 0x024b
- #define EINFG 0x0152
- #define ENTF 0x0153
- #define EINGABE 13
- #define ESC 27
-
- /* Zeichenfolgen-Dialoghinweise */
- char run_str[] =
- "AUSFÜHREN: S=Schneller L=Langsamer O=Optionen E=Ende";
-
- char edit_str[] =
- "BEARBEITEN: RICHTUNGSTASTEN=Bewegen UMSCHALT+RICHTUNGS=Bewegen/Umschalten\n LEERTASTE=Umschalten X=Löschen EINGABE=Fertig";
-
- char pause_str[] =
- "PAUSE: S=Start X=Löschen Z=Einzelschritt B=Bearbeiten E=Ende\n N=Neue Zufallsverteilung I=Einlesen R=Schreiben";
-
- char file_str[] = "Dateinamen eingeben: ";
-
- char ferr_str[] = "Dateizugriff versagt - weiter mit beliebiger Taste ...";
-
- char dense_str[] = "Ungültige Dichte - weiter mit beliebiger Taste ...";
-
- /* Funktionsprototypen */
- int main( int argc, char **argv );
- void run_mode( void );
- int pause_mode( void );
- void edit_mode( void );
- void init_life( void );
- void init_pfr( void );
- void zeichne_box( void );
- void init_mats( void );
- void zufall_vt( float chance );
- void generation( void );
- void pass2( void );
- void status_zelle( int aktion, int zle, int spl );
- int lese_life( void );
- int schrbe_life( void );
- int zeige_prompt( char *prompt, char antwort[] );
- void auffrischen( ZELLE einpuffer[], int far *auspuffer );
-
- /* main - Führt das Spiel Life aus.
- *
- * Parameter: argc - Anzahl der Befehlszeilenargumente
- * argv - Datenfeld aus Befehlszeilen-Zeichenfolgen
- *
- * Ergibt: 0
- *
- * Benutzt: grenze
- */
- int main( int argc, char **argv )
- {
-
- /* Video und Matrix initialisieren. Rahmen zeichnen. */
- init_life();
- init_pfr();
- zeichne_box();
- init_mats();
-
- /* Gibt es kein Befehlszeilenargument, endlos weiterfahren, sonst so
- * oft, wie auf Befehlszeile bestimmt. 0 auf der Befehlszeile bedeutet,
- * daß mit Bearbeiten statt Zufallszahlgeneration angefangen werden soll.
- */
- if( argc > 1 )
- {
- grenze = atol( argv[1] );
- endlos = FALSCH;
- }
- else
- {
- grenze = WAHR;
- endlos = WAHR;
- }
- if ( !grenze )
- {
- endlos = WAHR;
- edit_mode();
- }
- else
- zufall_vt( cfg.dichte );
-
- /* Life ausführen. */
- run_mode();
-
- /* Rücksetzen und beenden. */
- _settextcursor( cfg.cursor );
- _displaycursor( _GCURSORON );
- _setvideomode( _DEFAULTMODE );
- exit( FALSCH );
- }
-
- /* run_mode - Führt Life aus, prüft auf Tastatureingaben zwischen Genera-
- * tionen. Bei Tastatureingabe den entsprechenden Vorgang ausführen.
- *
- * Parameter: Keine
- *
- * Ergebnis: Keines
- *
- * Benutzt: grenze, endlos, run_str
- */
- void run_mode()
- {
- char taste;
- static clock_t speed = 100; /* Verspätung in Mikrosekonden (unter 1000) */
-
- /* Befehlsaufforderung anzeigen und auf Tastatureingabe prüfen. */
- zeige_prompt( run_str, "" );
- while( endlos || grenze-- )
- {
- delay( speed );
- generation();
- if( taste = getkey( NO_WAIT ) )
- {
- taste = toupper( taste );
- switch( taste )
- {
- case 'O': /* auf Pausenmodus */
- if( !pause_mode() )
- return;
- break;
- case 'S': /* Schneller */
- if( speed )
- speed -= 100;
- break;
- case 'L': /* Langsamer */
- if( speed < 1000 )
- speed += 100;
- break;
- case 'E': /* Ende */
- case ESC:
- return;
- }
- }
- }
- }
-
- /* pause_mode - Übernimmt eine Pausenmodus-Tastatureingabe und nimmt die
- * entsprechende Maßnahme.
- *
- * Parameter: Keine
- *
- * Ergibt: bei <E>nde FALSCH, sonst WAHR
- *
- * Benutzt: cfg und diverse Meldungs-Zeichenfolgen
- */
- int pause_mode()
- {
- int i, pause = WAHR;
- char tmp[80];
- char taste;
-
- zeige_prompt( pause_str, "" );
- while( pause )
- {
- taste = getkey( WAIT );
- switch( toupper( taste ) )
- {
-
- case 'X': /* Life-Arena löschen */
- init_pfr();
- zeichne_box();
- init_mats();
- break;
- case 'S': /* Start - Life neu starten */
- pause = FALSCH;
- break;
- case 'B': /* Bearbeiten des gegenwärtigen Musters */
- edit_mode();
- break;
- case 'E': /* Ende - das Spiel beenden */
- case ESC:
- return FALSCH;
- case 'Z': /* Einzelschritt - nur eine Generation */
- generation();
- grenze--;
- break;
- case 'N': /* Neue Zufallswerte erstellen */
- sprintf( tmp, "Gegenwärtige Dichte: %.f Neue Dichte: ",
- cfg.dichte * 100 );
- zeige_prompt( tmp, tmp );
- i = atoi( tmp );
- if ( (i < 1) || (i > 100) )
- {
- zeige_prompt( dense_str, "" );
- putch( SIGNAL );
- getch();
- zeige_prompt( pause_str, "" );
- break;
- }
- /* Bildschirm löschen und neu einrichten. */
- init_pfr();
- zeichne_box();
- init_mats();
- zeige_prompt( pause_str, "" );
- zufall_vt( cfg.dichte = (float)(i / 100.0) );
- break;
- case 'I': /* Neues Muster von Datei einlesen */
- if( !lese_life() )
- {
- zeige_prompt( ferr_str, "" );
- putch( SIGNAL );
- getch();
- }
- zeige_prompt( pause_str, "" );
- break;
- case 'R': /* Gegenwärtiges Muster zu Datei schreiben */
- if( !schrbe_life() )
- {
- zeige_prompt( ferr_str, "" );
- putch( SIGNAL );
- getch();
- }
- zeige_prompt( pause_str, "" );
- break;
- }
- }
- /* prompt wiederherstellen. */
- zeige_prompt( run_str, "" );
- return WAHR;
- }
-
- /* edit_mode - Übernimmt mehrfache Bearbeitungs-Tastatureingabe und nimmt die
- * entsprechende Maßnahme.
- *
- * Parameter: Keine
- *
- * Ergebnis: Keines
- *
- * Benutzt: grenze, cfg, edit_str, pause_str
- */
- void edit_mode()
- {
- int mehr = WAHR;
- unsigned taste;
- int curs_zle = cfg.mzeile / 2, curs_spl = cfg.msplt / 2;
-
- /* Dialoghinweis erneuern, Cursor anzeigen und mittigstellen. */
- zeige_prompt( edit_str, "" );
- _displaycursor ( _GCURSORON );
- _settextposition( curs_zle + 2, curs_spl + 2 );
-
- do {
- taste = getkey( WAIT );
- switch( taste )
- {
- case SH_HM: /* Nach links oben ('Nord-West') */
- case HM:
- if( (curs_spl > 0) && (curs_zle > 0) )
- {
- curs_spl--;
- curs_zle--;
- }
- break;
- case SH_UA: /* Gerade nach oben ('Norden') */
- case UA:
- case 'k':
- if( curs_zle > 0 )
- curs_zle--;
- break;
- case SH_PU: /* Nach rechts oben ('Nord-Osten') */
- case PU:
- if( (curs_spl < cfg.msplt - 1) && (curs_zle > 0) )
- {
- curs_spl++;
- curs_zle--;
- }
- break;
- case SH_RA: /* Nach rechst ('Osten') */
- case RA:
- case 'l':
- if( curs_spl < cfg.msplt - 1)
- curs_spl++;
- break;
- case SH_PD: /* Nach rechts-unten ('Süd-Osten') */
- case PD:
- if( (curs_spl < cfg.msplt - 1) && (curs_zle < cfg.mzeile - 1) )
- {
- curs_spl++;
- curs_zle++;
- }
- break;
- case SH_DA: /* Direkt nach unten ('Süden') */
- case DA:
- case 'j':
- if( curs_zle < cfg.mzeile - 1)
- curs_zle++;
- break;
- case SH_ED: /* Nach links-unten ('Süd-West') */
- case ED:
- if( (curs_spl > 0 ) && (curs_zle < cfg.mzeile - 1) )
- {
- curs_spl--;
- curs_zle++;
- }
- break;
- case SH_LA: /* Nach links ('Westen') */
- case LA:
- case 'h':
- if( curs_spl > 0 )
- curs_spl--;
- break;
- case ' ': /* Gegenwärtige Zelle umschalten */
- status_zelle( UMGESCH, curs_zle, curs_spl );
- break;
- case EINFG: /* Gegenwärtige Zelle einschalten */
- status_zelle( EIN, curs_zle, curs_spl );
- break;
- case ENTF: /* Gegenwärtige Zelle ausschalten */
- status_zelle( AUS, curs_zle, curs_spl );
- break;
- case 'X': /* Zellen löschen */
- case 'x':
- init_pfr();
- zeichne_box();
- init_mats();
- break;
- case 'F': /* Fertig - Änderungen annehmen */
- case 'f':
- case EINGABE:
- mehr = FALSCH;
- break;
- default: /* Unbekannte Tasten ignorieren */
- break;
- }
- /* Wenn UMSCHALTTASTE gedrückt, Taste umschalten. */
- if( (taste >> 8) == 2 )
- status_zelle( UMGESCH, curs_zle, curs_spl );
-
- /* Cursorposition neu einrichten. */
- _settextposition( curs_zle + 2, curs_spl + 2 );
-
- } while( mehr );
-
- /* Cursor ausschalten und Pausen-Dialoghinweis wiederherstellen. */
- _displaycursor (_GCURSOROFF );
- zeige_prompt( pause_str, "" );
- }
-
- /* init_life - Initialisiert Bildschirmmodus, Zeilen- und Cursor-Status.
- * Bestimmt Global-Variablen für Bildschirm, Konfiguration
- * und Life.
- *
- * Parameter: Keine
- *
- * Ergebnis: Keines
- *
- * Benutzt: Richtet folgende Werte ein:
- * cfg.rescan - Kennung für CGA-Handhabung
- * .cursor - Cusorform
- * .mzeile - Höchstanzahl der Zeilen
- * .videomem - Zeiger zum Bildschirm-Zwischenspeicher
- * .boxatrib - Vordergrund- u. Hintergundfarben des Rahmens
- * .hlfatrib - Farben der Hilfezeile
- * leben.atrib - Farben der lebendigen Zellen
- * tod.atrib - Farben der toten Zellen
- */
- void init_life()
- {
- struct videoconfig vc;
-
- /* Anfangs-Cursorposition speichern, und auf Blockform einrichten, dann
- * Cursor ausschalten.
- */
- cfg.cursor = _settextcursor( SETCURSOR( 0, 7 ) );
- _displaycursor( _GCURSOROFF );
-
- /* Konfiguration holen, und Variablen dem Adapter entsprechend einrichten. */
- _getvideoconfig( &vc );
- switch( vc.adapter )
- {
- case _CGA:
- cfg.rescan = WAHR;
- break;
- case _EGA:
- case _OEGA:
- cfg.mzeile = 39; /* 43-zeilig - 4 Zeilen */
- break;
- case _VGA:
- case _OVGA:
- cfg.mzeile = 46; /* 50-zeilig - 4 Zeilen */
- break;
- default:
- cfg.mzeile = 21; /* 25-zeilig - 4 Zeilen */
- break;
- }
-
- /* Variablen dem Vidomodus entsprechend einrichten. */
- switch( vc.mode )
- {
-
- case _HERCMONO:
- case _ERESNOCOLOR:
- case _TEXTMONO:
- _setvideomoderows( _TEXTMONO, cfg.mzeile + 4 );
- break;
-
- case _TEXTBW40:
- case _TEXTBW80:
- cfg.videomem = (int far *)0xb8000000;
- _setvideomoderows( _TEXTBW80, cfg.mzeile + 4 );
- break;
-
- default:
- cfg.videomem = (int far *)0xb8000000;
- cfg.boxatrib = SETATRIB( _TBRIGHTWHITE, _TBLUE );
- leben.atrib = tod.atrib = SETATRIB( _TWHITE, _TBLUE );
- cfg.hlfatrib = SETATRIB( _TWHITE, _TBLACK );
- _setvideomoderows( _TEXTC80, cfg.mzeile + 4 );
- break;
- }
- }
-
- /* init_pfr - Tote Zellen des Bildschirm-Zwischenspeichers initialisieren.
- *
- * Parameter: Keine
- *
- * Ergebnis: Keines
- *
- * Benutzt: bildpfr, cfg
- */
- void init_pfr()
- {
- register ZELLE *p = bildpfr;
-
- while( p < bildpfr + BBUFSIZE )
- *p++ = tod;
- }
-
-
- /* zeichne_box - ASCII-Grafikzeichen um den Rahmen des Bildschirm-Zwischen-
- * speichers zu zeichnen, dann den geänderten Zwischenspeicher zum
- * Bildschirm schreiben.
- *
- * Parameter: Keine
- *
- * Ergebnis: Keines
- *
- * Benutzt: bildpfr, cfg
- */
- void zeichne_box()
- {
- register unsigned char *p = (char *)bildpfr; /* Zeiger zum Zw-Speicher */
- int i, incr;
-
- /* Oberen Rahmen zeichnen. */
- *p = '┌';
- p +=2;
- for( i = 0; i < cfg.msplt; p += 2, i++ )
- *p = '─';
- *p = '┐';
- p += 2;
-
- /* Seitenrahmen zeichnen. */
- incr = (BSPLT - 1) * 2;
- for( i = 0; i < cfg.mzeile; p += (BSPLT * 2), i++ )
- {
- *p = '│';
- *(p + incr) = '│';
- }
-
- /* Untere Rahmenlinie zeichnen. */
- *p = '└';
- p += 2;
- for( i = 0; i < cfg.msplt; p += 2, i++)
- *p = '─';
- *p = '┘';
-
- /* Geänderten Bildschirm-Zwischenspeicher zum Videospeicher kopieren. */
- auffrischen( bildpfr, cfg.videomem );
- }
-
- /* init_mats - Initialisiert Matrizen von Life. Löscht Matrix 1 und 2,
- * initialisiert dann alle Zonen (1-9) von Matrix 1.
- *
- * Die "Zonen" dienen dem LIFE-Algorithmus beim Bestimmen der Methode zum
- * Berechnen der Nachbarn. Zonen sind wichtig für Ränder u. Ecken:
- *
- * +-+--------------+-+
- * |6| 2 |7|
- * +-+--------------+-+
- * | | | |
- * |4| 1 |5|
- * | | | |
- * +-+--------------+-+
- * |8| 3 |9|
- * +-+--------------+-+
- *
- * Zur leichteren Berechnung sind Zonen in Matrix 1 aufgezeichnet. Lebt eine
- * Zelle, wird ihre Existenz durch Hinzufügen von 100 gekennzeichnet.
- *
- * Parameter: Keine
- *
- * Ergebnis: Keines
- *
- * Benutzt: bildpfr, cfg
- */
- void init_mats()
- {
- int i, j; /* Schleifenzähler */
- char *p = (char *)mat1; /* Zeiger zu Matrix 1 */
-
- /* Zonen Matrix 1 mit 0 initialisieren. */
- memset( mat1, 0, cfg.mzeile * cfg.msplt );
- memset( mat2, 0, cfg.mzeile * cfg.msplt );
-
- /* Reihe 1 zu Zonen 6, 2 und 7 initialisieren. */
- *p++ = 6;
- for( i = 0; i < (cfg.msplt - 2); i++)
- *p++ = 2;
- *p++ = 7;
-
- /* Mittelzeilen 1 zu Zonen 4, 1 und 5 initialisieren. */
- for( j = 0; j < (cfg.mzeile - 2); j++ )
- {
- *p++ = 4;
- for( i = 0; i < (cfg.msplt - 2); i++ )
- *p++ = 1;
- *p++ = 5;
- }
-
- /* Letzte Zeilen zu Zonen 8, 3 und 9 initialisieren. */
- *p++ = 8;
- for( i = 0; i < (cfg.msplt - 2); i++ )
- *p++ = 3;
- *p++ = 9;
- }
-
- /* zufall_vt - Initialisiert die Zufallsverteilung der Zellen. Die Zellen
- * werden in Matrix 1 wie auch im Bildschirm-Zwischenspeicher mitgeschrieben.
- * Hat eine Zelle einen Zufallswert größer als die berechnete Verteilung, wird
- * ihrem Wert in Matrix 1 100 hinzugefügt, und sie wird zum Bildschirm-
- * Zwischenspeicher geschrieben.
- *
- * Parameter: chance - Prozent an Zufallseigenschaft
- *
- * Ergebniswert: Keiner
- *
- * Benutzt: bildpfr, cfg
- */
- void zufall_vt( float chance )
- {
- char *p = (char *)mat1; /* Zeiger zu Matrix 1 */
- register ZELLE *bp = bildpfr; /* Zeiger zu Bildschirm-Zwischenspeicher */
- int i, j; /* Schleifenzähler */
- int amt, rnd;
-
- amt = (int)(chance * 32768); /* Mindestwert, den Lebend-Zelle
- * überschreiten muß.
- */
- srand( (unsigned)time( NULL ) ); /* Anfangswert zu Zufallswerten machen */
- bp += BSPLT + 1; /* Start an erster Zelle, die nicht im Rahmen liegt */
-
- /* Jeder Zellen Status 'lebend' oder 'tot' zuweisen. */
- for( i = 0; i < cfg.mzeile; i++, bp += 2 )
- {
- for( j = 0; j < cfg.msplt; j++, p++, bp++ )
- {
- rnd = rand();
- if( rnd < amt )
- {
- *p += 100;
- *bp = leben;
- }
- }
- }
-
- /* Ergebnis am Bildschirm ausgeben. */
- auffrischen( bildpfr, cfg.videomem );
- }
-
- #define NW (-1-cfg.msplt) /* Richtungskonstanten innerhalb Matrix 2. */
- #define N (-cfg.msplt) /* Beispiel: NW (Nord-West) bedeutet den */
- #define NO (1-cfg.msplt) /* links darüberliegenden Nachbarn. */
- #define O (1)
- #define SO (1+cfg.msplt)
- #define S (cfg.msplt)
- #define SW (-1+cfg.msplt)
- #define W (-1)
-
- /* generation - Eine Generation von Life durchführen. Zuerst wird Matrix 2
- * gelöscht, dann Matrix 1 abgetastet. Wird auf lebendige Zelle gestoßen,
- * werden die ENTSPRECHENDEN NACHBARZELLEN IN MATRIX 2 um 1 erhöht, die
- * entsprechende Zelle darin selbst um 100. Bei toder Zelle nichts unter-
- * nehemen. Dies ist eine schnelle Art der 'Nachbarzählung', die in Matrix 2
- * mitverfolgt wird.
- *
- * Die "Zone" einer jeden Zelle wird geprüft und als Richtline zur Nachbar-
- * Bestimmung benutzt. Nachbarn der obersten Reihe liegen in der untersten
- * Reihe, d.h., Bildschirmgrenzen sind überwunden, indem eine jede Grenze
- * gleichzeitig zum Anfang auf der gegenüberliegenden Seite wird.
- *
- * Durchlauf 2 wird aufgerufen, um zu bestimmen, welche Zellen tatsächlich
- * leben oder sterben, je nach der Anzahl ihrer Nachbarn.
- *
- * Parameter: Keine
- *
- * Ergebnis: Keines
- *
- * Benutzt: bildpfr, cfg
- */
- void generation()
- {
- register char *p1; /* Zeiger zu Matrix 1 und 2 */
- register char *p2;
- int diff; /* Bytes zwischen Matrix 1 und 2 */
- int zone; /* Zone einer jeden Zelle */
- int mgroesse = MBUFSIZE;
-
- /* Matrix 2 löschen, und Abstand zwischen Zone 1 und 2 berechnen. */
- memset( mat2, 0, mgroesse );
- diff = (char *)mat2 - (char *)mat1;
-
- /* Für jede Zelle ... */
- for( p1 = (char *)mat1; p1 < (char *)mat1 + mgroesse; p1++ )
- {
- /* Wenn Zelle in Matrix 1 lebendig ist ... */
- if( *p1 > 100 )
- {
- /* Zu Zelle in Matrix 2 deuten und sie mit neuem Wert versehen. */
- p2 = p1 + diff;
- *p2 += 100;
-
- /* Zone holen und Nachbarn entsprechend fortschreiben. */
- zone = (*p1 - 100);
- switch( zone )
- {
- case 1:
- ++*(p2 + NW);
- ++*(p2 + N);
- ++*(p2 + NO);
- ++*(p2 + O);
- ++*(p2 + SO);
- ++*(p2 + S);
- ++*(p2 + SW);
- ++*(p2 + W);
- break;
- case 2:
- ++*(p2 + NW + mgroesse);
- ++*(p2 + N + mgroesse);
- ++*(p2 + NO + mgroesse);
- ++*(p2 + O);
- ++*(p2 + SO);
- ++*(p2 + S);
- ++*(p2 + SW);
- ++*(p2 + W);
- break;
- case 3:
- ++*(p2 + NW);
- ++*(p2 + N);
- ++*(p2 + NO);
- ++*(p2 + O);
- ++*(p2 + SO - mgroesse);
- ++*(p2 + S - mgroesse);
- ++*(p2 + SW - mgroesse);
- ++*(p2 + W);
- break;
- case 4:
- ++*(p2 + NW + cfg.msplt);
- ++*(p2 + N);
- ++*(p2 + NO);
- ++*(p2 + O);
- ++*(p2 + SO);
- ++*(p2 + S);
- ++*(p2 + SW + cfg.msplt);
- ++*(p2 + W + cfg.msplt);
- break;
- case 5:
- ++*(p2 + NW);
- ++*(p2 + N);
- ++*(p2 + NO - cfg.msplt);
- ++*(p2 + O - cfg.msplt);
- ++*(p2 + SO - cfg.msplt);
- ++*(p2 + S);
- ++*(p2 + SW);
- ++*(p2 + W);
- break;
- case 6:
- ++*(p2 + NW + mgroesse + cfg.msplt);
- ++*(p2 + N + mgroesse);
- ++*(p2 + NO + mgroesse);
- ++*(p2 + O);
- ++*(p2 + SO);
- ++*(p2 + S);
- ++*(p2 + SW + cfg.msplt);
- ++*(p2 + W + cfg.msplt);
- break;
- case 7:
- ++*(p2 + NW + mgroesse);
- ++*(p2 + N + mgroesse);
- ++*(p2 + NO + mgroesse - cfg.msplt);
- ++*(p2 + O - cfg.msplt);
- ++*(p2 + SO - cfg.msplt);
- ++*(p2 + S);
- ++*(p2 + SW);
- ++*(p2 + W);
- break;
- case 8:
- ++*(p2 + NW + cfg.msplt);
- ++*(p2 + N);
- ++*(p2 + NO);
- ++*(p2 + O);
- ++*(p2 + SO - mgroesse);
- ++*(p2 + S - mgroesse);
- ++*(p2 + SW + cfg.msplt - mgroesse);
- ++*(p2 + W + cfg.msplt);
- break;
- case 9:
- ++*(p2 + NW);
- ++*(p2 + N);
- ++*(p2 + NO - cfg.msplt);
- ++*(p2 + O - cfg.msplt);
- ++*(p2 + SO - mgroesse - cfg.msplt);
- ++*(p2 + S - mgroesse);
- ++*(p2 + SW - mgroesse);
- ++*(p2 + W);
- break;
- default:
- break;
- }
- } /* Ende if */
- } /* Ende for */
-
- /* pass2 zum Berechnen von 'Geburt' bzw. 'Tod' einer jeden Zellen aufrufen. */
- pass2();
- }
-
- /* pass2 - Matrix 2 absuchen, und Matrix 1 nach folgenden Kriterien anpassen:
- *
- * Wert Matrix 2 Ergebnis Matrix 1
- * -------------- ----------------------
- * 3 Tote Zelle wird lebendig
- * 102, 103 Keine Änderung
- * other > 100 Lebende Zelle 'stirbt'
- * other < 100 Keine Änderung
- *
- * Parameter: Keine
- *
- * Ergebnis: Keines
- *
- * Benutzt: bildpfr, cfg
- */
- void pass2()
- {
- register int i, j; /* Schleifen-Variablen */
- register char *p2= (char *)mat2; /* Zeiger zu Matrix 2 */
- ZELLE *bp = bildpfr; /* Zeiger Bildschirm-Zwischenspeicher */
- int diff; /* Abstand zwischen Matrix 1 und 2 */
-
- /* Rahmen zur ersten Zelle überspringen und Abstand zwischen
- * Matrix 1 und 2 berechnen
- */
- bp += BSPLT + 1;
- diff = (char *)mat2 - (char *)mat1;
-
- /* Äußere Schleife zählt Zeilen. */
- for( i = 0; i < cfg.mzeile; i++, bp += 2 )
- {
- /* Nächste Schleife zählt Spalten. */
- for( j = 0; j < cfg.msplt; j++, p2++, bp++ )
- {
- /* Wenn 3, lebende Zelle schreiben. */
- if( *p2 < 100 )
- {
- if( *p2 == 3 )
- {
- *(p2 - diff) += 100;
- *bp = leben;
- }
- }
- else
- /* Tote Zelle, wenn über 100 - außer 102 oder 103. */
- {
- if( (*p2 < 102) || (*p2 > 103) )
- {
- *(p2 - diff) -= 100;
- *bp = tod;
- }
- }
- }
- }
-
- /* Ergebnis zum Bildschirm ausgeben. */
- auffrischen( bildpfr, cfg.videomem );
- }
-
- /* status_zelle - Status einer bestimmten Zelle einrichten. Die Zelle kann
- * ein-, aus- oder umgeschaltet werden. Der Status wird in Matrix 1 und im
- * Bildschirm-Zwischenspeicher mitverändert.
- *
- * Parameter: aktion - AUS, EIN oder UMGESCH
- * zle
- * spl
- *
- * Ergebnis: Keines
- *
- * Benutzt: bildpfr, cfg
- */
- void status_zelle( int aktion, int zle, int spl )
- {
- register ZELLE *sp = bildpfr;
-
- /* Rahmen überspringen zur ersten Zelle. */
- sp += BSPLT + 1;
- sp += zle * BSPLT;
- sp += spl;
-
- /* Zellenstatus einrichten. */
- switch( aktion )
- {
- case AUS:
- mat1[zle][spl] -= 100;
- *sp = tod;
- break;
- case EIN:
- mat1[zle][spl] += 100;
- *sp = leben;
- break;
- case UMGESCH:
- if( mat1[zle][spl] > 100 )
- {
- mat1[zle][spl] -= 100;
- *sp = tod;
- }
- else
- {
- mat1[zle][spl] += 100;
- *sp = leben;
- }
- break;
- }
-
- /* Ergebnis am Bildschirm ausgeben. */
- auffrischen( bildpfr, cfg.videomem );
- }
-
- /* zeige_prompt - Ausgabe eines Dialoghinweises unten auf dem Bildschirm.
- * Wird ein Zwischenspeicher ungleich Null als Antwort übergeben, wird darauf
- * gewartet, daß der Benutzer eine Zeichenfolge eingibt.
- *
- * Parameter: prompt - Dialoghinweis oder Hilfe-Zeichenfolge
- * antwort - Zwischenspeicher für Antwort-Zeichenfolge
- * (NULL = keine Antwort)
- *
- * Ergibt: WAHR, wenn keine Antwort erwartet oder richtige Antwort
- * eingegangen ist; bei ungültiger Antwort FALSCH.
- */
- int zeige_prompt( char *prompt, char antwort[] )
- {
- char tmp[159];
-
- /* Alten Dialoghinweis löschen und neuen schreiben. */
- memset( tmp, ' ', 158 );
- tmp[158] = 0;
- _settextcolor( cfg.hlfatrib & 0x00ff );
- _setbkcolor( (long)(cfg.hlfatrib >> 4) );
- _settextposition( BREIH + 1, 1 );
- _outtext( tmp );
- _settextposition( BREIH + 1, 1 );
- _outtext( prompt );
-
- /* Wurde Antwort-Zwischenspeicher übergeben, Antwort dafür holen. */
- if ( *antwort )
- {
- _displaycursor( _GCURSORON );
- tmp[0] = 158;
- cgets( tmp );
- strcpy( antwort, tmp + 2 );
-
- _displaycursor( _GCURSOROFF );
- if( *antwort )
- return WAHR;
- else
- return FALSCH;
- }
- else
- return WAHR;
- }
-
- /* lese_life - Liest ein Life-Muster von Datei ein. Das Muster besteht aus
- * Bitcodes: Bit geladen = Leben, Bit gelöscht = Tod. Enthält die Datei mehr
- * Zellen, als der Bildschirm, werden die Extra-Zellen ignoriert. Dies kann
- * vorkommen, wenn ein Muster in 25-Zeilenmodus gespeichert wurde, aber in
- * 43-Zeilenmodus eingelesen wird.
- *
- * Parameter: Keine
- *
- * Ergibt: WAHR wenn erfolgreich, sonst FALSCH
- *
- * Benutzt: bildpfr, cfg
- */
- int lese_life()
- {
- ZELLE *scrnp = bildpfr; /* Zeiger zum Bildschirm-Zwischenspeicher */
- char *matp = (char *)mat1; /* Zeiger zu Matrix 1 */
- unsigned char mbyte, fbyte; /* Masken-Byte und Lese-Byte */
- int i, j, k; /* Schleifenzähler */
- int mehr = WAHR; /* Fortsetzungs-Kennung */
- FILE *filep; /* Zeiger zu file struct */
- char fname[81]; /* Dateinamen-Zwischenspeicher */
-
- /* FALSCH ergeben, wenn Dialoghinweis oder Öffnen der Datei fehlschlägt. */
- if( !zeige_prompt( file_str, fname ) )
- return FALSCH;
- if( !(filep = fopen( fname, "rb" )) )
- return FALSCH;
-
- /* Zwischenspeicher, Bildschirm und Zeiger initialisieren. */
- init_pfr();
- init_mats();
- zeichne_box();
- scrnp += BSPLT + 1;
-
- /* Masken-Byte initialisieren und erstes Byte lesen. */
- mbyte = 0x80;
- fread( (void *)&fbyte, 1, 1, filep );
-
- /* Zeilen zählen ... */
- for( i = 0, k = 0; (i < cfg.mzeile) && mehr; i++, scrnp += 2 )
- {
- /* Spalten zählen ... */
- for( j = 0; (j < cfg.msplt) && mehr; j++, scrnp++, matp++)
- {
- /* Ist Bit 'ein', die Zelle als lebendig kennzeichnen. */
- if( mbyte & fbyte )
- {
- *matp += 100;
- *scrnp = leben;
- }
-
- /* Maske anpassen und nötigenfalls weiteres Byte einlesen. */
- mbyte >>= 1;
- if( ++k > 7 )
- {
- k = 0;
- mbyte = 0x80;
-
- /* Beenden, wenn Datei keine weiteren Bytes enthält. */
- if( !fread( (void *)&fbyte, 1, 1, filep ) )
- mehr = FALSCH;
- }
- }
- }
-
- /* Am Bildschirm anzeigen, und Datei schließen. */
- auffrischen( bildpfr, cfg.videomem );
- fclose( filep );
- return WAHR;
- }
-
- /* schrbe_life - Schreibt ein Life-Muster zur Datei. Das Muster besteht aus
- * Bitcodes: Bit geladen = Leben, Bit gelöscht = Tod.
- *
- * Parameter: Keine
- *
- * Ergibt: WAHR wenn erfolgreich, sonst FALSCH
- *
- * Benutzt: bildpfr, cfg
- */
- int schrbe_life()
- {
- char *matp = (char *)mat1; /* Zeiger zu Matrix 1 */
- unsigned char mbyte, fbyte; /* Masken-Byte und Lese-Byte */
- int i, j, k; /* Schleifenzähler */
- FILE *filep; /* Zeiger zu file struct */
- char fname[81]; /* Dateinamen-Zwischenspeicher */
-
- /* FALSCH ergeben, wenn Dialoghinweis oder Öffnen der Datei fehlschlägt. */
- if( !zeige_prompt( file_str, fname ) )
- return FALSCH;
- if( !(filep = fopen( fname, "wb" )) )
- return FALSCH;
-
- /* Masken-Byte initialisieren und erstes Byte lesen. */
- mbyte = 0x80;
- fbyte = k = 0;
-
- /* Zeilen zählen ... */
- for( i = 0; i < cfg.mzeile; i++)
- {
- /* Spalten zählen ... */
- for( j = 0; j < cfg.msplt; j++, matp++)
- {
- /* Ist Zelle lebendig, Bit einschalten. */
- if( *matp > 100 )
- fbyte += mbyte;
-
- /* Maske anpassen, und nötigenfalls weiteres Byte schreiben. */
- mbyte >>= 1;
- if( ++k > 7 )
- {
- fwrite( (void *)&fbyte, 1, 1, filep );
- fbyte = k = 0;
- mbyte = 0x80;
- }
- }
- }
-
- /* Sicherstellen, daß alle bis einschl. des letzten Bytes geschrieben
- * sind, dann Datei schließen.
- */
- if( k > 0 )
- fwrite( (void *)&fbyte, 1, 1, filep );
- fclose( filep );
- return WAHR;
- }
-
- /* auffrischen - Schreibt Zwischenspeicher mit den Zellen zum eigentlichen
- * Video-Zwischenspeicher. Für CGA beim Kopieren auf rescan anpassen,
- * sonst direkt kopieren. Die CGA-Variante funktioniert nur in Assembler
- * schnell genug.
- *
- * Parameter: einpuffer - interner Zwischenspeicher mit den Zellen
- * auspuffer - Zeiger zum Hardware-Videospeicher
- *
- * Ergebnis: Keines
- *
- * Benutzt: cfg
- */
- void auffrischen( ZELLE einpuffer[], int far *auspuffer )
- {
- int bgroesse = BBUFSIZE;
-
- _asm \
- {
- mov si, einpuffer ; Quelle laden = Bildschirmspeicher
- les di, auspuffer ; Ziel laden = Videospeicher
- mov cx, bgroesse ; Zeilen * Spalten
- cld ; DF = 0 (Richtungskennung)
-
- cmp cfg.rescan, FALSCH ; Rescan nur prüfen, falls CGA
- je nichtcga
-
- mov dx, 03DAh
- warten0:
- sti
- nop
- cli
- lodsw ; Zeichen laden und in BX speichern
- mov bx, ax
-
- warten1: in al, dx ; Warten, bis Horizontale aktiv ist
- shr al, 1
- jc warten1
- cli
-
- warten2: in al, dx ; Warten, bis Horizontale nicht aktiv ist (retrace)
- shr al, 1
- jnc warten2
-
- mov ax, bx ; Zeichen wiederherstellen und
- stosw ; zu Videospeicher verlegen
- sti
-
- loop warten0 ; Nächstes
- jmp SHORT getout ; Fertig mit CGA
-
- nichtcga: ; Wenn nicht CGA,
- rep movsw ; ganzen Bildschirm ohne Warten kopieren
- getout:
-
- }
- }
-