webentwicklung-frage-antwort-db.com.de

Konvertierung von Bitboard zu Titboard (ternäres Bitboard)

In vielen Brettspielen (wie Checkers, Go und Othello/Reversi) kann jedes Feld durch drei Zustände dargestellt werden: weiß, schwarz oder leer.

8x8-Karten in solchen Spiel-Engines werden normalerweise als zwei Bitboards dargestellt: eine 64-Bit-Ganzzahl für die Position von weißen Teilen und eine weitere 64-Bit-Ganzzahl für Schwarz.

Wenn jedoch lokale Spielmuster gespeichert werden, kann eine solche binäre Darstellung viel Platz erfordern. Das Erstellen einer Nachschlagetabelle für alle möglichen Werte einer 8-Quadrat-Zeile würde beispielsweise ein Array mit 256 * 256 = 4 ^ 8 = 65536 Werten erfordern, verglichen mit nur 3 ^ 8 = 6561 möglichen Positionen (da ein Quadrat niemals sein kann) besetzt mit schwarzen und weißen Stücken).

Eine alternative Möglichkeit besteht darin, die Boards als ternäre Zahlen zu speichern - so genannte Titboards . Aber ich habe nirgendwo einen schnellen Algorithmus gefunden, um zwischen der Darstellung zweier binärer Ganzzahlen und der Darstellung ternärer Ganzzahlen zu konvertieren.

Daher ist meine Frage

Gibt es eine effiziente Möglichkeit, zwei sich gegenseitig ausschließende Binärzahlen (w & b == 0) in ternäre Zahlen umzuwandeln (zu codieren), so dass jedes eindeutige Paar solcher Ganzzahlen einer resultierenden eindeutigen Ganzzahl zugeordnet wird? (Vorzugsweise in C/C++.)

Python-Beispiel

Hier ist meine Python-Lösung, um dies zu tun:

white_black_empty = lambda w, b: int(format(w, 'b'), base=3) + \
                                 int(format(b, 'b'), base=3)*2

Beispielwerte:

  • w = 10100012 = 81
  • b = 01001002 = 36
  • ergebnis = 10100013 + 01001003* 2 = 10100013 + 02002003 = 12102013 = 1315

Also white_black_empty(81, 36) == 1315.

Die Umwandlung einer Ganzzahl in eine Zeichenfolgendarstellung einer binären format(x, 'b') und dann von einer Zeichenfolge zurück in eine Ganzzahl unter Verwendung der Basis 3 int(x, base=3) sieht jedoch ziemlich ineffizient aus.

7
Andriy Makukha

Wie wäre es, wenn Sie speichern, was Sie konvertieren möchten? Mit dem folgenden Schema würden jede weiteren 8 Bits einer Zeile 512 Zahlen in einem Array (oder einer Hash-Tabelle) kosten. Der Kompromiss wäre mehr Additionen und Bitextraktion, um den Speicher zu kürzen - zum Beispiel, um 8 Bits zu speichern, anstatt die vollen 8, was zu 255 Zahlen führt, könnten wir 2 ^ 4 und 2 ^ 4 (für den zweiten Satz von 4 Bit), was dazu führt, dass 32 (plus 32 für die schwarzen Zahlen) gespeichert werden, wobei jedoch jeder Satz von 4 Bit und eine weitere Addition während der Konvertierung extrahiert werden müssen.

const ones = new Array(256);
const twos = new Array(256);

for (let i=0; i<256; i++){
  let one = 0;
  let two = 0;

  for (let j=0; j<8; j++){
    if ((1 << j) & i){
      one += Math.pow(3, j);
      two += 2*Math.pow(3, j);
    }
    ones[i] = one;
    twos[i] = two;
  }
}

function convert(w, b){
  return ones[w] + twos[b];
}

console.log(convert(81, 36));
2

Wenn Ihre Hardware eine schnelle Popcount - Operation hat, können Sie eine Tafel mit n Leerzeichen als 2 n-Bit-Werte ⟨Maske, _ darstellen.Farbe⟩, wobei der zweite Wert garantiert im Bereich [0, 2 liegtpopcount (Maske)] Der erste Wert ist 1 an der Bitposition, die einem Quadrat entspricht, wenn das Quadrat belegt ist; der zweite Wert ist 1 an der Bitposition, die j entspricht, wenn das jte belegte Quadrat ein weißes Stück hat. Um auf Bits in Farbe zuzugreifen, ist es nützlich, diese Funktion zu haben, die eine Bitposition in Farbe bei Maske und eine Bitposition in der Maske zurückgibt, die entspricht auf ein 1-Bit in der Maske (dh ein Bit, das einem belegten Quadrat entspricht):

static inline int colourBitPos(unsigned mask, unsigned pos) {
  return popcount(mask & ((1U << pos) - 1));
}

(Mit anderen Worten, es zählt die Anzahl von 1 Bits in Maske nach der angegebenen Position.)

Sie können das⟩ Maske, Farbe⟩-Paar dann einfach in eine einzelne Zahl im Bereich [0, 3] umwandelnn-1] über eine vorberechnete Nachschlagetabelle, die Basisindizes enthält. Als ich ursprünglich an dieses System dachte, dachte ich an n + 1 verkettete Tabellen, die jeweils einer einzelnen Popcount entsprechen. Das ist konzeptionell schön, da die Anzahl der möglichen Farben eines Codes mit popcount i offensichtlich 2 istich während die Anzahl der Masken mit Popcount iC(n, i) ist (mitC() als Binomialkoeffizientenfunktion, da es hier kein MathJax gibt). Die schöne Identität:

 Sum of binomial coefficients multiplied by powers of two is a power of three

ist wahrscheinlich weniger bekannt als andere binomische Identitäten.

Während es möglich ist, diese Anordnung zu nutzen, um den Index in O (n) - Zeit (Stück für Stück im Feld Maske) ziemlich mühsam zu berechnen, ist die einfachste und schnellste Lösung eine 2 verwendenn-element feste Nachschlagetabelle Basis, deren Größe viel kleiner als die 3 istn Datentabellen. Für jeden Wert von Maske wird ein Basiswert berechnet, indem für jeden Wert einfach die entsprechende Zweierpotenz addiert wird:

int base[1U<<N];
for (unsigned i = 0, offset = 0; i < 1U<<N; ++i) {
  base[i] = offset;
  offset += 1U<<popcount(i);
}

Dann kann der Index eines beliebigen Paares wie folgt berechnet werden:

index = base[mask] + colour;

[Siehe Beispiel unten]

Die Zweikomponentendarstellung ist nicht besonders schwierig zu handhaben, obwohl sie offensichtlich nicht so einfach ist wie eine Zwei-Bit-Drei-Wege-Wahl. Um zum Beispiel herauszufinden, was im Quadrat i steht, gehen Sie wie folgt vor:

(mask & (1U << i))
    ? (colour & ((1U << colouredBitPos(mask, i) - 1) ? WHITE : BLACK
    : EMPTY

In einem anderen Beispiel würden Sie Folgendes tun, um ein Teil mit der Farbe col (WHITE = 1, BLACK = 0) zum derzeit nicht belegten Quadrat i hinzuzufügen:

unsigned pos = colouredBitPos(mask, i);
colour += (colour & ~((1U << pos) - 1)) + (col << pos);
mask |= 1U << i;

effektives Verschieben des ersten Teils von Farbe um ein Bit nach links, um das neue Bit einzufügen. Wenn der Platz bereits belegt wäre, würden Sie die Verschiebung vermeiden:

unsigned pos = colouredBitPos(mask, i);
colour &= ~(1U << pos);  // Make it black
colour |= col << pos;    // And give it the right colour

Andere Operationen sind ähnlich einfach.

Ob diese Arbeit in Ihrem Fall gerechtfertigt ist, hängt von so vielen Faktoren ab, dass ich unmöglich ein Urteil fällen kann. Der Platzbedarf ist jedoch nahezu optimal. Der einzige Zusatzaufwand, abgesehen von einer erhöhten Codegröße, ist eine einzelne 2n-element Nachschlagetabelle, die für alle Datentabellen verwendet werden kann und die in jedem Fall im Verhältnis zur Größe einer Datentabelle winzig ist (z. B. für n = 8 haben die Datentabellen 6561 Elemente, sodass eine Nachschlagetabelle mit 256 Elementen einen Mehraufwand von 4% für eine einzelne Datentabelle verursacht, deren Datenelemente ebenfalls Kurzschlüsse sind, und die Nachschlagetabelle nicht beibehalten werden muss, wenn Sie die Datentabellen beibehalten, da sie leicht neu generiert werden kann .)


Beispiel für die Indexkodierung:

Mit n = 4 lautet die Basis-Nachschlagetabelle der Einfachheit halber:

mask   base      mask   base      mask   base      mask   base
0000      0      0100      9      1000     27      1100     45
0001      1      0101     11      1001     29      1101     49
0010      3      0110     15      1010     33      1110     57
0011      5      0111     19      1011     37      1111     65

Unter Verwendung von U = Nicht belegt, B = Schwarz, W = Weiß (und unter der Annahme, dass Weiß wie oben 1 ist), einige Beispielcodierungen und -indizes:

board   mask  colour    compute index   decimal
UUBW    0011      01    base[0011]+ 01 =   6
UUWB    0011      10    base[0010]+ 10 =   7
WUBW    1011     101    base[1011]+101 =  42
4
rici

Das Konvertieren von Zeichenfolge in Ganzzahl und zurück ist in der Tat ineffizient.

Wenn Sie die Werte nur verschlüsseln müssen, ist es hilfreich, wenn Sie sie anhand der tatsächlichen Zahlen, die sie darstellen, betrachten. Wenn Sie beispielsweise acht Zeilen auf einer Tafel betrachten, lautet der Status der ersten Position effektiv boardState % 3; Wir können die Konvention anwenden, dass ein schwarzes Teil auf einem 1, ein weißes Teil auf einem 2 und ein leerer Wert auf einem 0 vorhanden ist. Für das zweite wird es zu (boardState % 9)/3, das dritte zu (boardState % 27) / 3 und so weiter.

Für die Codierung können wir diese Überlegung also erweitern: Wir nehmen entweder eine 0, 1 oder 2, multiplizieren sie mit 3 und addieren sie zu einer "Ergebnis" -Zahl. Ein (SEHR ungetesteter) Beispielcode ist unten:

#include <inttypes.h>
#include <math.h>

uint64_t tritboard(uint64_t white, uint64_t black){
    uint64_t onemask = 0x0000000000000001;//you could also just say "= 1"
    uint64_t retval = 0;
    uint64_t thisPos;

    for(char i = 0; i < 8; i++){
        thisPos = 0;
        if(white & (oneMask << i)) thisPos += 2;
        if(black & (oneMask << i)) thisPos += 1;

        retval += thisPos * ( (uint64_t) pow(3, i));
    }//for

    return retval;
}//tritboard

Leider werden Sie mit Computern, die teilweise binär sind, nur in der Lage sein, Bitverschiebungen so clever anzugehen. Daher ist die for-Schleife in diesem Code (der in C etwas weniger grob ist als in Python, was die Leistung betrifft).

Beachten Sie, dass der Umfang dieses Ansatzes für Sie begrenzt ist. Wie Sie wissen, können Sie mit diesem Ansatz nicht die gesamte Karte darstellen (da für eine 64-Bit-Ganzzahl keine 3 ^ 64-Werte möglich sind).

Hoffentlich ist das für Sie zugänglicher als der String-Ansatz!

1
Taylor Nelms

In der Praxis sollten Sie den Board-Status in Base-4 in unsigned longs speichern, wobei jede Board-Zeile mit einer ganzzahligen Anzahl von unsigned longs aufgefüllt wird. Dies gibt Ihnen die beste Speicherlokalität, sehr schnellen Zugriff auf Platinenzellen, verbraucht aber 26,2% mehr RAM als ternäres Packen.

Um den Kartenstatus in einer Binärdatei zu speichern, können Sie 5 ternäre Ziffern (fünf Zellenstatus der Karte) in jedes 8-Bit-Byte packen. Dies verbraucht nur 5,1% mehr Speicher als das ternäre Packen und ist einfach und robust zu implementieren. Insbesondere müssen Sie sich auf diese Weise nicht um die Bytereihenfolge (Endianness) kümmern.

Das Problem bei der reinen ternären Packung besteht darin, dass jede Ziffer zur Basis 3 die meisten Binärziffern betrifft, die denselben numerischen Wert darstellen. Zum Beispiel 38 = 300000003 = 6561 = 11001101000012. Dies bedeutet, dass die einzige praktische Möglichkeit zum Extrahieren von 3-Basis-Ziffern die wiederholte Division und das Modul (durch 3) ist.

Um eine Tafel der Größe zu beschreiben N×Mwird die ternäre Pack- und Entpackfunktion im wesentlichen sein O(N2M2) und daher mit zunehmender Platinengröße immer langsamer. Sie werden wahrscheinlich bessere Einsparungen erzielen, wenn Sie eine Komprimierungsbibliothek (z. B. liblzma ) mit weniger CPU-Zeit verwenden. Bei vielen Platinenkonfigurationen funktioniert möglicherweise auch die Lauflängencodierung.

Hier ist eine Beispielimplementierung für Boards mit bis zu 16777215 × 16777215 Zellen (getestet nur mit bis zu 32768 × 32768 Zellen):

#include <stdlib.h>
#include <inttypes.h>
#include <limits.h>
#include <stdio.h>
#include <time.h>

#define  ULONG_BITS   (CHAR_BIT * sizeof (unsigned long))
#define  ULONG_CELLS  (CHAR_BIT * sizeof (unsigned long) / 2)

struct board {
    int              rows;
    int              cols;
    size_t           stride;
    unsigned long   *data;
};

enum {
    EMPTY = 0, /* calloc() clears the data to zeroes */
    WHITE = 1,
    BLACK = 2,
    ERROR = 3
};

int board_init(struct board *const b, const int rows, const int cols)
{
    const size_t  stride = (cols + ULONG_CELLS - 1) / ULONG_CELLS;
    const size_t  ulongs = stride * (size_t)rows;

    if (b) {
        b->rows   = 0;
        b->cols   = 0;
        b->stride = 0;
        b->data   = NULL;
    }

    if (!b || rows < 1 || cols < 1)
        return -1;

    if ((size_t)(ulongs / stride) != (size_t)rows)
        return -1;

    b->data = calloc(ulongs, sizeof b->data[0]);
    if (!b->data)
        return -1;

    b->rows = rows;
    b->cols = cols;
    b->stride = stride;

    return 0;
}

static inline int  get_cell(const struct board *const b, const int row, const int col)
{
    if (!b || row < 0 || col < 0 || row >= b->rows || col >= b->cols)
        return EMPTY;
    else {
        const size_t         i =  (size_t)col / ULONG_CELLS;
        const size_t         c = ((size_t)col % ULONG_CELLS) * 2;
        const unsigned long  w = b->data[b->stride * row + i];
        return (w >> c) & 3;
    }
}

static inline int  set_cell(struct board *const b, const int row, const int col, const int value)
{
    if (!b || row < 0 || col < 0 || row >= b->rows || col >= b->cols)
        return EMPTY;
    else {
        const size_t    i =  (size_t)col / ULONG_CELLS;
        const size_t    c = ((size_t)col % ULONG_CELLS) * 2;
        unsigned long  *w = b->data + b->stride * row + i;

        *w = ((*w) & (3uL << c)) | ((unsigned long)(value & 3) << c);
        return value & 3;
    }
}

static inline int  write_u24(FILE *const out, const int value)
{
    unsigned int  u = value;

    if (!out || value < 0 || value > 16777215 || ferror(out))
        return -1;

    if (fputc(u & 255, out) == EOF)
        return -1;
    else
        u >>= 8;

    if (fputc(u & 255, out) == EOF)
        return -1;
    else
        u >>= 8;

    if (fputc(u & 255, out) == EOF)
        return -1;
    else
        return 0;
}

static inline int  read_u24(FILE *const in, unsigned int *const to)
{
    unsigned int  result;
    int           c;

    if (!in || ferror(in))
        return -1;

    c = fgetc(in);
    if (c == EOF)
        return -1;
    else
        result = c & 255;

    c = fgetc(in);
    if (c == EOF)
        return -1;
    else
        result |= (c & 255) << 8;

    c = fgetc(in);
    if (c == EOF)
        return -1;
    else
        result |= (c & 255) << 16;

    if (to)
        *to = result;

    return 0;
}

int board_save(const struct board *const b, FILE *const out)
{
    int row, col, cache, coeff;

    if (!b || !out || ferror(out) || !b->stride ||
        b->rows < 1 || b->rows > 16777215 ||
        b->cols < 1 || b->cols > 16777215)
        return -1;

    if (write_u24(out, b->rows))
        return -1;
    if (write_u24(out, b->cols))
        return -1;

    /* Clear byte cache. */
    cache = 0;
    coeff = 1;

    for (row = 0; row < b->rows; row++) {
        for (col = 0; col < b->cols; col++) {
            switch (get_cell(b, row, col)) {
            case EMPTY: /* Saved as 0 */
                break;
            case WHITE: /* Saved as 1 */
                cache += coeff;
                break;
            case BLACK: /* Saved as 2 */
                cache += coeff + coeff;
                break;
            default: /* Invalid cell state. */
                return -1;
            }

            if (coeff >= 81) {
                if (fputc(cache, out) == EOF)
                    return -1;
                cache = 0;
                coeff = 1;
            } else
                coeff *= 3;
        }
    }

    if (coeff > 1)
        if (fputc(cache, out) == EOF)
            return -1;

    if (fflush(out))
        return -1;

    return 0;
}

int board_load(struct board *const b, FILE *in)
{
    unsigned int  rows, cols, row, col, cache, count;
    int           c;

    if (b) {
        b->rows   = 0;
        b->cols   = 0;
        b->stride = 0;
        b->data   = NULL;
    }

    if (!b || !in || ferror(in))
        return -1;

    if (read_u24(in, &rows) || rows < 1 || rows > 16777215)
        return -1;
    if (read_u24(in, &cols) || cols < 1 || cols > 16777215)
        return -1;

    if (board_init(b, rows, cols))
        return -1;

    /* Nothing cached at this point. */
    cache = 0;
    count = 0;

    for (row = 0; row < rows; row++) {
        for (col = 0; col < cols; col++) {

            if (count < 1) {
                c = fgetc(in);
                if (c == EOF || c < 0 || c >= 243)
                    return -1;

                cache = c;
                count = 5;
            }

            switch (cache % 3) {
            case 0: /* Leave as background. */
                break;
            case 1: /* White */
                if (set_cell(b, row, col, WHITE) != WHITE)
                    return -1;
                break;                
            case 2: /* Black */
                if (set_cell(b, row, col, BLACK) != BLACK)
                    return -1;
                break;
            }

            cache /= 3;
            count--;

        }
    }

    /* No errors. */
    return 0;
}


/* Xorshift 64* pseudo-random number generator. */
static uint64_t  prng_state = 1;

static inline uint64_t  prng_randomize(void)
{
    int       rounds = 1024;
    uint64_t  state;

    state = (uint64_t)time(NULL);

    while (rounds-->0) {
        state ^= state >> 12;
        state ^= state << 25;
        state ^= state >> 27;
    }

    if (!state)
        state = 1;

    prng_state = state;
    return state;
}

static inline uint64_t  prng_u64(void)
{
    uint64_t  state = prng_state;
    state ^= state >> 12;
    state ^= state << 25;
    state ^= state >> 27;
    prng_state = state;
    return state * UINT64_C(2685821657736338717);
}

/* Uniform random ternary generator. */
static uint64_t  ternary_cache = 0;
static int       ternary_bits  = 0;

static inline int prng_ternary(void)
{
    int  retval;

    do {

        if (ternary_bits < 2) {
            ternary_cache = prng_u64();
            ternary_bits = 64;
        }

        retval = ternary_cache & 3;
        ternary_cache >>= 1;
        ternary_bits -= 2;

    } while (retval > 2);

    return retval;
}


int main(int argc, char *argv[])
{
    struct board  original, reloaded;
    uint64_t      correct, incorrect, count[3];
    double        percent;
    FILE         *file;
    int           rows, cols, row, col;
    char          dummy;

    if (argc != 4) {
        fprintf(stderr, "\n");
        fprintf(stderr, "Usage: %s [ -h | --help ]\n", argv[0]);
        fprintf(stderr, "       %s FILENAME ROWS COLUMNS\n", argv[0]);
        fprintf(stderr, "\n");
        fprintf(stderr, "This program generates a random ternary board,\n");
        fprintf(stderr, "saves it to file FILENAME, reads it back, and\n");
        fprintf(stderr, "verifies that the board state is intact.\n");
        fprintf(stderr, "\n");
        return EXIT_SUCCESS;
    }

    if (!argv[1][0]) {
        fprintf(stderr, "No filename specified.\n");
        return EXIT_FAILURE;
    }

    if (sscanf(argv[2], "%d %c", &rows, &dummy) != 1 || rows < 1 || rows > 16777215) {
        fprintf(stderr, "%s: Invalid number of rows.\n", argv[2]);
        return EXIT_FAILURE;
    }

    if (sscanf(argv[3], "%d %c", &cols, &dummy) != 1 || cols < 1 || cols > 16777215) {
        fprintf(stderr, "%s: Invalid number of columns.\n", argv[2]);
        return EXIT_FAILURE;
    }

    if (board_init(&original, rows, cols)) {
        fprintf(stderr, "Cannot create a board with %d rows and %d columns.\n", rows, cols);
        return EXIT_FAILURE;
    }

    fprintf(stderr, "Filling board with a random state; random seed is %" PRIu64 ".\n", prng_randomize());

    percent = 100.0 / (double)rows / (double)cols;

    count[0] = count[1] = count[2] = 0;
    for (row = 0; row < rows; row++)
        for (col = 0; col < cols; col++) {
            int t = prng_ternary();
            if (t < 0 || t > 3) {
                fprintf(stderr, "prng_ternary() returned %d!\n", t);
                return EXIT_FAILURE;
            }
            count[t]++;
            set_cell(&original, row, col, t);
        }

    fprintf(stderr, "   Empty: %" PRIu64 " cells, %.3f%%.\n", count[EMPTY], (double)count[EMPTY] * percent);
    fprintf(stderr, "   White: %" PRIu64 " cells, %.3f%%.\n", count[WHITE], (double)count[WHITE] * percent);
    fprintf(stderr, "   Black: %" PRIu64 " cells, %.3f%%.\n", count[BLACK], (double)count[BLACK] * percent);

    file = fopen(argv[1], "wb");
    if (!file) {
        fprintf(stderr, "%s: Cannot open file for writing.\n", argv[1]);
        return EXIT_FAILURE;
    }

    fprintf(stderr, "Saving to %s.\n", argv[1]);

    if (board_save(&original, file)) {
        fclose(file);
        fprintf(stderr, "Write error.\n");
        return EXIT_FAILURE;
    }
    if (fclose(file)) {
        fprintf(stderr, "Write error.\n");
        return EXIT_FAILURE;
    }

    fprintf(stderr, "Reloading game board.\n");

    file = fopen(argv[1], "rb");
    if (!file) {
        fprintf(stderr, "%s: Cannot open file for reading.\n", argv[1]);
        return EXIT_FAILURE;
    }

    if (board_load(&reloaded, file)) {
        fclose(file);
        fprintf(stderr, "Read error.\n");
        return EXIT_FAILURE;
    }
    if (fclose(file)) {
        fprintf(stderr, "Read error.\n");
        return EXIT_FAILURE;
    }

    if (original.rows != reloaded.rows) {
        fprintf(stderr, "Row count mismatches.\n");
        return EXIT_FAILURE;
    } else
    if (original.cols != reloaded.cols) {
        fprintf(stderr, "Column count mismatches.\n");
        return EXIT_FAILURE;
    }

    fprintf(stderr, "Comparing board states.\n");

    correct = 0;
    incorrect = 0;
    for (row = 0; row < rows; row++)
        for (col = 0; col < cols; col++)
            if (get_cell(&original, row, col) == get_cell(&reloaded, row, col))
                correct++;
            else
                incorrect++;

    if (incorrect) {
        fprintf(stderr, "Found %" PRIu64 " mismatching cells (%.3f%%).\n", incorrect, (double)incorrect * percent);
        return EXIT_FAILURE;
    }

    if (correct != (uint64_t)((uint64_t)rows * (uint64_t)cols)) {
        fprintf(stderr, "Internal bug in the board comparison double loop.\n");
        return EXIT_FAILURE;
    }

    fprintf(stderr, "Verification successful; functions work as expected for a board with %d rows and %d columns.\n", rows, cols);

    return EXIT_SUCCESS;
}

Die Funktion board_init() initialisiert eine Karte, board_save() speichert einen Kartenzustand in einem Stream, einschließlich der Kartengröße, im portablen Binärformat (jede Datei generiert dieselbe Karte auf Big-Endian- und Little-Endian-Architekturen), und board_load() lädt a zuvor gespeichertes Board aus einem Stream. Sie alle geben bei Erfolg 0 zurück, bei Fehlern ungleich null.

Die Funktionen get_cell() und set_cell() sind statische Inline-Accessorfunktionen, mit denen der Status einzelner Zellen in einer Platine überprüft und festgelegt werden kann.

Wie ich anfangs angedeutet habe, werden in RAM (4 Zellen pro Byte) und in einer Datei 5 Zellen pro Byte gespeichert.

Das Beispielprogramm verwendet drei Befehlszeilenparameter: einen Dateinamen, die Anzahl der Zeilen und die Anzahl der Spalten. Es generiert einen zufälligen Zustand dieser Größe, speichert ihn in der genannten Datei, liest ihn aus der genannten Datei in eine separate Karte zurück und vergleicht schließlich die Kartenzustände, um zu überprüfen, ob die implementierten Funktionen korrekt zu funktionieren scheinen.

1
Nominal Animal