0

I am trying to get my Langton's Ant program working, but it has some odd behavior that I can't quite understand. For each new iteration, I don't see the chain of events mentioned in the wikipedia article:

Simplicity. During the first few hundred moves it creates very simple patterns which are often symmetric.

Chaos. After a few hundred moves, a large, irregular pattern of black and white squares appears. The ant traces a pseudo-random path until around 10,000 steps.

Emergent order. Finally the ant starts building a recurrent "highway" pattern of 104 steps that repeats indefinitely.

I generate an ant with a random y, x, and direction each time. Why is it the case then that this is my order of events:

  • Builds a semi-scrambled pattern
  • Makes a small staircase
  • Shifts down and left, and repeats the same small structure

It seems like it is making a sort of highway, but this one is definitely not 104 steps, and it only creates one unique pattern per time that the program is run. And there is no occurrence of grand chaos that I typically see in Langton's Ant demos. I have looked at my code very closely and I cannot find out why it is performing so oddly. Perhaps I should initialize some squares as black when I begin? I am not sure. Please let me know if you know what I am doing wrong.

Here is how I am compiling (on macOS): clang -O3 -lncurses -o langton langton.c

#include <curses.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

typedef enum {Left, Down, Right, Up} Orientation;
typedef struct {int y, x; Orientation o;} Ant;

int rand_num(int lower, int upper) {return (rand() % (upper - lower + 1)) + lower;}

int main() {
    initscr();
    cbreak();
    noecho();
    int max_y, max_x;
    getmaxyx(stdscr, max_y, max_x);
    start_color();
    use_default_colors();
    init_pair(1, COLOR_BLACK, COLOR_BLACK);
    init_pair(2, COLOR_RED, COLOR_RED);
    srand(time(NULL));

    int** grid = malloc(max_y * sizeof(int*));
    for (int y = 0; y < max_y; y++) {
        int* row = malloc(max_x * sizeof(int));
        memset(row, 0, max_x);
        grid[y] = row;
    }

    Ant ant = {rand_num(0, max_y - 1), rand_num(0, max_x - 1), rand_num(0, 3)};
    for (int i = 0; i < 10000; i++) {
        int cell = grid[ant.y][ant.x];

        ant.o += !cell ? 1 : -1; // turn ant
        if (ant.o < Left) ant.o = Up;
        else if (ant.o > Up) ant.o = Left;

        grid[ant.y][ant.x] = !cell; // invert cell

        switch (ant.o) { // move forward
            case Up: ant.y--; break;
            case Down: ant.y++; break;
            case Left: ant.x--; break;
            case Right: ant.x++; break;
        }

        if (ant.y == max_y || ant.y < 0 || ant.x == max_x || ant.x < 0) break; // break if outside bounds
    }
    clear();
    for (int y = 0; y < max_y; y++) {
        for (int x = 0; x < max_x; x++) {
            if (grid[y][x]) {
                attron(COLOR_PAIR(1));
                mvprintw(y, x, " ");
                attroff(COLOR_PAIR(1));
            }
        }
    }
    attron(COLOR_PAIR(2));
    mvprintw(ant.y, ant.x, " ");
    attroff(COLOR_PAIR(2));
    getch();
    for (int y = 0; y < max_y; y++) free(grid[y]);
    free(grid);
    endwin();
}
Caspian Ahlberg
  • 934
  • 10
  • 19
  • 'Please let me know if you know what I am doing wrong': not using a debugger and not logging/printing var values. – Martin James Dec 05 '20 at 21:55
  • @MartinJames I don't have any base values for any "correct" ant movement to compare my program to. Even if I needed to print variable values for debugging, I couldn't because *curses* takes up the terminal screen. The fact is that I cannot find a fault in my algorithm, so that is why I am asking here. – Caspian Ahlberg Dec 05 '20 at 22:42
  • the wiki article says each turn is "turn, flip, move". you are doing "move, flip, turn". – mr. fixit Dec 06 '20 at 02:41
  • @mr.fixit Thank you, I updated my code with this. Good catch. But for some reason, it still behaves the same. – Caspian Ahlberg Dec 06 '20 at 04:28
  • 1
    i noticed a couple things: you're calling memset wrong, final parameter should be 'max_x * sizeof(int)'. you're randomizing the start and orientation. make your life easier. start should be 'mid_x, mid_y'. orientation should be something fixed. once you've fixed those, then step through the code and confirm that at the end of each turn, the ant's position and orientation agree with what you think they should be. once you get the first 20 turns doing exactly what they should, THEN let it rip with 10,000 (or whatever) iterations. – mr. fixit Dec 08 '20 at 03:58
  • 1
    Thank you. This fixed a screen sizing problem that I was also having. Otherwise, I also fixed the main bug: I was checking if the ant's orientation was smaller than `Left`. Clang makes an enum's members unsigned, so it was not possible for `Left` to be smaller than 0. I increased each member's value by one and that solved it. – Caspian Ahlberg Dec 08 '20 at 18:19

0 Answers0