Matrix Simulation processes each cell according to rules, usually based on its neighbors.
Focus on recognizing:
“Next state of the grid” / “update all cells simultaneously” → neighbor rules + (often) state encoding
Pattern 1: Neighbor Counting
Watch Game of Life rules decide one step of a 3×3 board — a survivor with 3 neighbors and a reproduction at the corner. Press ▶ to animate.
⚠️ Animation & Content Notice
The animation work is not fully finished — some animations may have slight errors.
If there is a major error in the content or if the animation or content is difficult to understand, please contact us at rayyancodingschool@gmail.com.
Conway's Game of Life
Simulate one generation of Conway's Game of Life. A live cell with 2–3 live neighbors survives; a dead cell with exactly 3 live neighbors becomes alive; all others die or stay dead. The next state is computed from a copy of the current grid.
Grid: 1 = alive, 0 = dead. Count each cell's 8 live neighbors against the current grid, then apply the rules. The final step reveals the next generation. Watch the live center survive (3 neighbors) and the dead corner (0,0) become born (3 neighbors).
1
for each cell: count live neighbors (8 directions)
2
live cell + 2 or 3 neighbors → stays alive
3
dead cell + exactly 3 neighbors → becomes alive
4
else → dead next round (compute from a COPY)
The universal building block — 8-direction deltas:
private static final int[][] DIRS = {
{-1, -1}, {-1, 0}, {-1, 1},
{ 0, -1}, { 0, 1},
{ 1, -1}, { 1, 0}, { 1, 1}
};
public int countLiveNeighbors(int[][] grid, int r, int c) {
int rows = grid.length, cols = grid[0].length;
int live = 0;
for (int[] dir : DIRS) {
int nr = r + dir[0];
int nc = c + dir[1];
if (nr >= 0 && nr < rows && nc >= 0 && nc < cols
&& grid[nr][nc] == 1) {
live++;
}
}
return live;
}DIRS = [(-1, -1), (-1, 0), (-1, 1),
(0, -1), (0, 1),
(1, -1), (1, 0), (1, 1)]
def count_live_neighbors(grid, r, c):
rows, cols = len(grid), len(grid[0])
return sum(
1
for dr, dc in DIRS
if 0 <= r + dr < rows and 0 <= c + dc < cols
and grid[r + dr][c + dc] == 1
)static const int DIRS[8][2] = {
{-1, -1}, {-1, 0}, {-1, 1},
{ 0, -1}, { 0, 1},
{ 1, -1}, { 1, 0}, { 1, 1}
};
int countLiveNeighbors(vector<vector<int>>& grid, int r, int c) {
int rows = grid.size(), cols = grid[0].size();
int live = 0;
for (auto& dir : DIRS) {
int nr = r + dir[0], nc = c + dir[1];
if (nr >= 0 && nr < rows && nc >= 0 && nc < cols
&& grid[nr][nc] == 1)
live++;
}
return live;
}const DIRS = [
[-1, -1],
[-1, 0],
[-1, 1],
[0, -1],
[0, 1],
[1, -1],
[1, 0],
[1, 1],
];
function countLiveNeighbors(grid, r, c) {
const rows = grid.length,
cols = grid[0].length;
let live = 0;
for (const [dr, dc] of DIRS) {
const nr = r + dr,
nc = c + dc;
if (nr >= 0 && nr < rows && nc >= 0 && nc < cols && grid[nr][nc] === 1)
live++;
}
return live;
}Bounds check first, then the condition — every grid simulation starts here.
Pattern 2: Game of Life (In-Place State Encoding)
The catch: cells update simultaneously, but later cells still need the OLD values. Trick — encode transitions in place:
2 = was alive → dies 3 = was dead → becomes alive
Values 1 and 2 both mean “was alive” when counting neighbors:
public void gameOfLife(int[][] board) {
int rows = board.length, cols = board[0].length;
for (int r = 0; r < rows; r++) {
for (int c = 0; c < cols; c++) {
int live = countLive(board, r, c);
if (board[r][c] == 1 && (live < 2 || live > 3))
board[r][c] = 2; // alive → dead
else if (board[r][c] == 0 && live == 3)
board[r][c] = 3; // dead → alive
}
}
for (int r = 0; r < rows; r++)
for (int c = 0; c < cols; c++)
if (board[r][c] == 2) board[r][c] = 0;
else if (board[r][c] == 3) board[r][c] = 1;
}
private int countLive(int[][] board, int r, int c) {
int rows = board.length, cols = board[0].length, live = 0;
for (int dr = -1; dr <= 1; dr++) {
for (int dc = -1; dc <= 1; dc++) {
if (dr == 0 && dc == 0) continue;
int nr = r + dr, nc = c + dc;
if (nr >= 0 && nr < rows && nc >= 0 && nc < cols
&& (board[nr][nc] == 1 || board[nr][nc] == 2)) {
live++; // 1 or 2 = was alive
}
}
}
return live;
}def game_of_life(board):
rows, cols = len(board), len(board[0])
def count_live(r, c):
total = 0
for dr in (-1, 0, 1):
for dc in (-1, 0, 1):
if dr == dc == 0:
continue
nr, nc = r + dr, c + dc
if 0 <= nr < rows and 0 <= nc < cols \
and board[nr][nc] in (1, 2): # was alive
total += 1
return total
for r in range(rows):
for c in range(cols):
live = count_live(r, c)
if board[r][c] == 1 and live not in (2, 3):
board[r][c] = 2 # alive → dead
elif board[r][c] == 0 and live == 3:
board[r][c] = 3 # dead → alive
for r in range(rows):
for c in range(cols):
if board[r][c] == 2:
board[r][c] = 0
elif board[r][c] == 3:
board[r][c] = 1int countLive(vector<vector<int>>& board, int r, int c) {
int rows = board.size(), cols = board[0].size(), live = 0;
for (int dr = -1; dr <= 1; dr++) {
for (int dc = -1; dc <= 1; dc++) {
if (dr == 0 && dc == 0) continue;
int nr = r + dr, nc = c + dc;
if (nr >= 0 && nr < rows && nc >= 0 && nc < cols
&& (board[nr][nc] == 1 || board[nr][nc] == 2))
live++; // 1 or 2 = was alive
}
}
return live;
}
void gameOfLife(vector<vector<int>>& board) {
int rows = board.size(), cols = board[0].size();
for (int r = 0; r < rows; r++) {
for (int c = 0; c < cols; c++) {
int live = countLive(board, r, c);
if (board[r][c] == 1 && (live < 2 || live > 3))
board[r][c] = 2; // alive → dead
else if (board[r][c] == 0 && live == 3)
board[r][c] = 3; // dead → alive
}
}
for (auto& row : board)
for (auto& cell : row) {
if (cell == 2) cell = 0;
else if (cell == 3) cell = 1;
}
}function gameOfLife(board) {
const rows = board.length,
cols = board[0].length;
const countLive = (r, c) => {
let live = 0;
for (let dr = -1; dr <= 1; dr++) {
for (let dc = -1; dc <= 1; dc++) {
if (dr === 0 && dc === 0) continue;
const nr = r + dr,
nc = c + dc;
if (
nr >= 0 &&
nr < rows &&
nc >= 0 &&
nc < cols &&
(board[nr][nc] === 1 || board[nr][nc] === 2)
)
live++; // 1 or 2 = was alive
}
}
return live;
};
for (let r = 0; r < rows; r++) {
for (let c = 0; c < cols; c++) {
const live = countLive(r, c);
if (board[r][c] === 1 && (live < 2 || live > 3)) board[r][c] = 2;
else if (board[r][c] === 0 && live === 3) board[r][c] = 3;
}
}
for (let r = 0; r < rows; r++)
for (let c = 0; c < cols; c++) {
if (board[r][c] === 2) board[r][c] = 0;
else if (board[r][c] === 3) board[r][c] = 1;
}
}Simultaneous updates + in-place requirement → encode old→new in spare bits, decode on a second pass.
Pattern 3: Sudoku Validation
No neighbors here — track what has already appeared per row, column and 3×3 box:
public boolean isValidSudoku(char[][] board) {
Set<String> seen = new HashSet<>();
for (int r = 0; r < 9; r++) {
for (int c = 0; c < 9; c++) {
char val = board[r][c];
if (val == '.') continue;
String rowKey = "row" + r + val;
String colKey = "col" + c + val;
String boxKey = "box" + (r / 3) + (c / 3) + val;
if (!seen.add(rowKey) || !seen.add(colKey)
|| !seen.add(boxKey)) {
return false;
}
}
}
return true;
}def is_valid_sudoku(board):
seen = set()
for r in range(9):
for c in range(9):
val = board[r][c]
if val == ".":
continue
keys = (
("row", r, val),
("col", c, val),
("box", r // 3, c // 3, val),
)
for key in keys:
if key in seen:
return False
seen.add(key)
return Truebool isValidSudoku(vector<vector<char>>& board) {
unordered_set<string> seen;
for (int r = 0; r < 9; r++) {
for (int c = 0; c < 9; c++) {
char val = board[r][c];
if (val == '.') continue;
string keys[3] = {
"row" + to_string(r) + val,
"col" + to_string(c) + val,
"box" + to_string(r / 3) + to_string(c / 3) + val
};
for (auto& key : keys) {
if (!seen.insert(key).second) return false;
}
}
}
return true;
}function isValidSudoku(board) {
const seen = new Set();
for (let r = 0; r < 9; r++) {
for (let c = 0; c < 9; c++) {
const val = board[r][c];
if (val === ".") continue;
for (const key of [
`row${r}${val}`,
`col${c}${val}`,
`box${(r / 3) | 0}${((c / 3) | 0)}${val}`,
]) {
if (seen.has(key)) return false;
seen.add(key);
}
}
}
return true;
}Box index
(r/3, c/3)maps each cell to its 3×3 quadrant — the whole trick.
Common Mistakes
Overwriting cells during simultaneous updates.
Game of Life breaks unless you encode transitions (or copy the board).
Forgetting bounds checks.
Every neighbor access needs 0 <= nr < rows && 0 <= nc < cols.
Wrong box index in Sudoku.
(r / 3, c / 3) — not (r % 3, c % 3).
Complexity
| Pattern | Time | Space |
|---|---|---|
| Neighbor count | O(n·m·8) | O(1) |
| Game of Life | O(n·m) | O(1) in-place |
| Sudoku validate | O(81) | O(81) |
Premium Content
Unlock Matrix Simulation and all premium lessons with a subscription.
From ₹199.99/year — See plans