Skip to main content

How to Create a Conway's Game of Life Simulation in Batch Script

Let's build a working simulation of John Conway's famous "Game of Life" using nothing but Windows Batch Scripting. While Batch is primarily known for file management, system administration, and network configuration, stretching its capabilities to simulate a cellular automaton is one of the most rewarding challenges a developer can undertake.

By attempting to build this mathematical simulation, you will master advanced array emulation, variable scoping, nested loops, and screen rendering techniques in a language that does not natively support complex data structures. Whether you are an entry level scripter trying to understand logic loops or a mid level developer pushing the boundaries of the Command Prompt, this guide will walk you through every step.

Understanding Conway's Game of Life

For those unfamiliar, the Game of Life is a zero player game. Its evolution is determined entirely by its initial state, requiring no further input. The game takes place on an infinite two dimensional orthogonal grid of square "cells," each of which is in one of two possible states: live or dead.

Every cell interacts with its eight neighbors (horizontal, vertical, and diagonal). At each step in time, the following transitions occur:

  1. Underpopulation: Any live cell with fewer than two live neighbors dies.
  2. Survival: Any live cell with two or three live neighbors lives on to the next generation.
  3. Overpopulation: Any live cell with more than three live neighbors dies.
  4. Reproduction: Any dead cell with exactly three live neighbors becomes a live cell.

Translating this into Batch script involves emulating a grid system and repeatedly calculating the next state without bogging down the console.

Initializing the Grid System

Because Batch does not have native 2D arrays, we must simulate them using dynamic variable names. For a grid with a width W and height H, we can define variables like cell_x_y where x and y are the coordinates.

@echo off
setlocal enabledelayedexpansion

:: Define the width and height of our grid
set "WIDTH=15"
set "HEIGHT=10"

:: Initialize all cells to dead (0)
for /L %%y in (1, 1, %HEIGHT%) do (
for /L %%x in (1, 1, %WIDTH%) do (
set "cell_%%x_%%y=0"
)
)

echo Grid initialized successfully.
pause
info

Always use setlocal enabledelayedexpansion when dealing with dynamic variables within loops. It allows you to use the ! character to evaluate variables at execution time rather than parse time.

Seeding the Initial State

A game of life is boring without life. Before the simulation loop begins, we need to populate the array with our starting "live" cells. For a simple demonstration, a common oscillator structure known as a "Blinker" is drawn by placing three live cells in a row.

:: Seed a Blinker
set "cell_5_5=1"
set "cell_6_5=1"
set "cell_7_5=1"

:: Seed a 'Glider'
set "cell_2_1=1"
set "cell_3_2=1"
set "cell_1_3=1"
set "cell_2_3=1"
set "cell_3_3=1"

Creating the Render Engine

To see the cells, we must draw them on the screen. We iterate through every row and column, building a string for each row before echoing it out. A dead cell can be represented by a space or dot, and a live cell by a solid block character like O or an ASCII block.

:RENDER
cls
for /L %%y in (1, 1, %HEIGHT%) do (
set "row="
for /L %%x in (1, 1, %WIDTH%) do (
if "!cell_%%x_%%y!"=="1" (
set "row=!row!O"
) else (
set "row=!row!."
)
)
echo !row!
)

The Logic Loop: Calculating Generations

The trickiest part of the simulation is calculating the next generation. We cannot update the cell_x_y variables immediately as we check them, because that would corrupt the state of neighboring cells that still need to be checked against the current generation.

To solve this, we must read from the current array and write the new statuses to a completely separate "next generation" array.

Counting Neighbors

To count neighbors for a specific cell at (%%x, %%y) inside the generation loop, we first calculate the four directional offsets. Because Batch does not support nested delayed expansion (writing !cell_!xm1!_!ym1!! causes the ! delimiters to pair incorrectly), we resolve the offset values into a separate set of for loop variables using for /f.

set /a "xm1=%%x-1, xp1=%%x+1, ym1=%%y-1, yp1=%%y+1"
set "neighbors=0"

:: Resolve offsets into for-loop variables to avoid nested expansion
for /f "tokens=1-4" %%a in ("!xm1! !xp1! !ym1! !yp1!") do (
:: %%a=x-1 %%b=x+1 %%c=y-1 %%d=y+1

:: Check top row (y-1)
if "!cell_%%a_%%c!"=="1" set /a "neighbors+=1"
if "!cell_%%x_%%c!"=="1" set /a "neighbors+=1"
if "!cell_%%b_%%c!"=="1" set /a "neighbors+=1"

:: Check middle row, excluding self
if "!cell_%%a_%%y!"=="1" set /a "neighbors+=1"
if "!cell_%%b_%%y!"=="1" set /a "neighbors+=1"

:: Check bottom row (y+1)
if "!cell_%%a_%%d!"=="1" set /a "neighbors+=1"
if "!cell_%%x_%%d!"=="1" set /a "neighbors+=1"
if "!cell_%%b_%%d!"=="1" set /a "neighbors+=1"
)

Applying the Rules

With the neighbor count in hand, we apply Conway's rules.

set "curr_state=!cell_%%x_%%y!"
set "next_state=0"

if "!curr_state!"=="1" (
if !neighbors! equ 2 set "next_state=1"
if !neighbors! equ 3 set "next_state=1"
) else (
if !neighbors! equ 3 set "next_state=1"
)

:: Save to the new buffer
set "next_cell_%%x_%%y=!next_state!"

Common Wrong Cases and Best Practices

A recurring stumbling block when dealing with grid based algorithms in Batch is how boundary bounds are handled, commonly leading to incorrect simulations or script crashes.

The Wrong Way: Ignoring Grid Boundaries

When developers attempt to access cells beyond the defined map size, like checking y-1 when y=1, Batch attempts to look up a variable named cell_0_0. If that variable was not strictly defined, Batch evaluates it as empty space. Mixing empty variables into mathematical comparisons breaks the syntax.

Wrong Code Example:

:: Loop calculates y-1, which results in 0
set "y=1"
set /a "ym1=y-1"

:: If cell_5_0 is undefined, this throws a syntax error
if !cell_5_%ym1%! equ 1 set /a "neighbors+=1"

The Correct Way: Strict Boolean Fallbacks

To handle variables that might not exist outside the grid bounds, never use mathematical comparators (equ) on raw grid variables. Always use string comparison, which evaluates gracefully even if the variable name evaluates to an empty string.

Correct Code Example:

:: Loop calculates y-1, which results in 0
set "y=1"
set /a "ym1=y-1"

:: Using double quotes and string comparison ensures empty strings don't break the script
if "!cell_5_%ym1%!"=="1" (
set /a "neighbors+=1"
)

By keeping the state variables strictly as characters ("1" or "0") and using string evaluations ==, "out of bounds" neighbors are safely evaluated as undefined and ignored.

Full Script Implementation

Putting the entire process together, we get a functional Game of Life loop. Simply copy and paste the below code into a file named life.bat and run it.

@echo off
setlocal enabledelayedexpansion
title Conway's Game of Life in Batch
mode con cols=45 lines=15

:: Configuration
set "WIDTH=20"
set "HEIGHT=10"

:: 1. Initialize empty grid
:: Rows 0 and HEIGHT+1, columns 0 and WIDTH+1 act as a dead boundary
set /a "HBOUND=HEIGHT+1"
set /a "WBOUND=WIDTH+1"
for /L %%y in (0, 1, !HBOUND!) do (
for /L %%x in (0, 1, !WBOUND!) do (
set "cell_%%x_%%y=0"
)
)

:: 2. Seed Initial Life (A Glider)
set "cell_2_1=1"
set "cell_3_2=1"
set "cell_1_3=1"
set "cell_2_3=1"
set "cell_3_3=1"

:GAMELOOP
:: 3. Render the Screen
cls
for /L %%y in (1, 1, %HEIGHT%) do (
set "row="
for /L %%x in (1, 1, %WIDTH%) do (
if "!cell_%%x_%%y!"=="1" (
set "row=!row![]"
) else (
set "row=!row!.."
)
)
echo !row!
)

:: 4. Calculate Next Generation
for /L %%y in (1, 1, %HEIGHT%) do (
for /L %%x in (1, 1, %WIDTH%) do (

set /a "xm1=%%x-1, xp1=%%x+1, ym1=%%y-1, yp1=%%y+1"
set "neighbors=0"

for /f "tokens=1-4" %%a in ("!xm1! !xp1! !ym1! !yp1!") do (
if "!cell_%%a_%%c!"=="1" set /a "neighbors+=1"
if "!cell_%%x_%%c!"=="1" set /a "neighbors+=1"
if "!cell_%%b_%%c!"=="1" set /a "neighbors+=1"

if "!cell_%%a_%%y!"=="1" set /a "neighbors+=1"
if "!cell_%%b_%%y!"=="1" set /a "neighbors+=1"

if "!cell_%%a_%%d!"=="1" set /a "neighbors+=1"
if "!cell_%%x_%%d!"=="1" set /a "neighbors+=1"
if "!cell_%%b_%%d!"=="1" set /a "neighbors+=1"
)

set "curr_state=!cell_%%x_%%y!"
set "next_state=0"

if "!curr_state!"=="1" (
if !neighbors! equ 2 set "next_state=1"
if !neighbors! equ 3 set "next_state=1"
) else (
if !neighbors! equ 3 set "next_state=1"
)

set "next_cell_%%x_%%y=!next_state!"
)
)

:: 5. Copy buffer to active grid
for /L %%y in (1, 1, %HEIGHT%) do (
for /L %%x in (1, 1, %WIDTH%) do (
set "cell_%%x_%%y=!next_cell_%%x_%%y!"
)
)

:: Wait briefly
ping 127.0.0.1 -n 2 >nul

goto GAMELOOP
warning

Because Batch files interpret commands line by line through CMD, a very large grid (e.g., 50x50) will cause massive rendering lags. It is recommended to keep WIDTH and HEIGHT under 30 for smooth animations.

Conclusion

Building Conway's Game of Life in Batch is not the fastest approach compared to languages like C or Python, but it remains one of the most effective ways to push the limits of your syntax knowledge. By successfully maintaining twin array buffers, securely navigating string equality limits, and generating primitive graphical outputs on the command prompt console, you demonstrate a robust grasp on data structures spanning far past conventional automation tasks.