# tags: automata, cell, life, awk, ANSI, code # # an awk adaptation of conway's game of life 4x... # Michael Sanders 2023 # https://busybox.neocities.org/notes/automata.txt # # requires the sleep command & an ANSI capable terminal, # invoke script as: awk -v SEED=$RANDOM -f automata.txt # # notes... # # an array of 4 'colonies', along with some metrics # for each group is displayed on the current TTY, # to exit, press & hold key-combo CONTROL+C till awk # receives signal SIGINT # # script will auto-exit once all 4 colonies are extinct # but note, a given colony may reach stasis (equilibrium) # & defy expectations, guess that's life... # # further reading... # # https://en.wikipedia.org/wiki/Cellular_automaton # https://en.wikipedia.org/wiki/Conway's_Game_of_Life BEGIN { POP = 0.36 # initial population density RATE = 1 # rate of refresh GRID = 0 # if '1' displays grid background CELL = "#" # character used to depict a cell ROWS = 9; COLS = 21 # colony dimensions, best let it be # seed srand with value passed to script else use default if (length(SEED) != 5) SEED = 93458; srand(SEED) # starting positions for colonies startRows[1] = 3; startCols[1] = 4 startRows[2] = 3; startCols[2] = 28 startRows[3] = 13; startCols[3] = 4 startRows[4] = 13; startCols[4] = 28 # initialize each colony for (c = 1; c <= 4; c++) { for (x = 1; x <= ROWS; x++) { for (y = 1; y <= COLS; y++) { colony[c, x, y] = (rand() < POP ? 1 : 0) } } } printf "\033[H\033[2J\033[?25l" # clear screen & hide cursor printf "\033[%d;%dHCellular Automata Experiment in AWK | ^C=Break | Seed: %05d | Gen: ", 2, 3, SEED while (1) { allDead = 1 buf = "\033[?25l" # hide cursor for (c = 1; c <= 4; c++) { livingCells[c] = deadCells[c] = 0 buf = buf sprintf("\033[%d;%dH%d", startRows[c] + 1, startCols[c] - 1, c) for (x = 1; x <= ROWS; x++) { for (y = 1; y <= COLS; y++) { buf = buf sprintf("\033[%d;%dH", startRows[c] + x, startCols[c] + y) if (colony[c, x, y] == 1) { buf = buf sprintf("\033[3%dm%s\033[0m", int(rand() * 6) + 1, CELL) allDead = 0 livingCells[c]++ } else { tmp = (GRID == 1 ? "." : " ") buf = buf tmp deadCells[c]++ } } } buf = getMetrics(buf, c, livingCells[c], deadCells[c], ++g) } if (allDead) { buf = buf "\033[24;0HAll colonies dead. Exiting..." print buf break } # calculate next state for each colony for (c = 1; c <= 4; c++) { if (livingCells[c] > 0) { for (x = 1; x <= ROWS; x++) { for (y = 1; y <= COLS; y++) { colony[c, x, y] = nextState(c, x, y) } } } } printf "%s", buf system("sleep " RATE) } printf "\033[?25h" # show cursor before exiting } function getNeighborCount(c, x, y, liveNeighbors, nx, ny) { liveNeighbors = 0 for (nx = x - 1; nx <= x + 1; nx++) { for (ny = y - 1; ny <= y + 1; ny++) { if ((nx != x || ny != y) && colony[c, nx, ny] == 1) liveNeighbors++ } } return liveNeighbors } function nextState(c, x, y, liveNeighbors, nx, ny, state) { liveNeighbors = getNeighborCount(c, x, y) state = colony[c, x, y] if (state == 1 && (liveNeighbors < 2 || liveNeighbors > 3)) return 0 if (state == 0 && liveNeighbors == 3) return 1 return state } function getMetrics(str, colony, living, dead, g, x, y) { x = 3 + 5 * (colony - 1) # position str = str sprintf("\033[%d;%dH%08d", 2, 71, g / 4) if (living > 0) { str = str sprintf("\033[%d;%dHColony %d", x + 1, 52, colony) str = str sprintf("\033[%d;%dHCells Living: %d ", x + 2, 52, living) str = str sprintf("\033[%d;%dHCells Dead: %d ", x + 3, 52, dead) str = str sprintf("\033[%d;%dHDensity: %.2f%% ", x + 4, 52, (living / (ROWS * COLS)) * 100) } else { str = str sprintf("\033[%d;%dHColony %d Extinct...", x + 1, 52, colony) for (y = 2; y <= 4; y++) str = str sprintf("\033[%d;%dH%-19s", x + y, 52, "") } return str } # eof