Concept
In this assignment, I tried to use Conway’s Game of Life to emulate how a bacterial colony grows in a petri dish. The aim was to blend the classic algorithm with a twist, introducing alternating rulesets based on iterations and a hexagonal pattern. This would allow the sketch to resemble a bacterial colony growing and dying out, leading to an ever-evolving and visually appealing display.
Sketch
User Interaction
- The user can spawn new living cells by holding down the mouse button and moving it around.
- There is a slider at the bottom that alters the framerate/speed of the sketch
Code
- Creating the Hexagonal Grid
function drawHexagon(x, y, size) { beginShape(); for (let i = 0; i < 6; i++) { let angle = TWO_PI / 6 * i; let vx = x + cos(angle) * size; let vy = y + sin(angle) * size; vertex(vx, vy); } endShape(CLOSE); }
This function draws a hexagon on the canvas. It starts by creating a shape and iterates six times (once for each side of the hexagon). In each iteration, it calculates the vertex coordinates using trigonometric functions (cos
and sin
) and then adds these vertices to the shape. Finally, the shape is closed to complete the hexagon.
- Updating the Grid State
function updateGrid() { for (let col = 0; col < cols; col++) { for (let gridRow = 0; gridRow < rows; gridRow++) { let neighbors = calculateNeighbors(col, gridRow); let newCellState = calculateNewCellState(grid[col][gridRow], neighbors); nextGrid[col][gridRow] = newCellState; } } // Swap grids for next iteration let temp = grid; grid = nextGrid; nextGrid = temp.map(gridRow => [...gridRow]); }
The updateGrid
function is the core of the simulation. It loops through each cell in the grid and determines its next state (alive or dead) based on the current state and the number of neighbors. This is done by calling calculateNeighbors
to count living neighbors and calculateNewCellState
to apply the Game of Life rules. After calculating the new states for all cells, the function swaps the current and next grids, preparing for the next iteration.
- Calculating Neighboring Cells
function calculateNeighbors(col, gridRow) { let count = 0; let neighborPositions = col % 2 === 0 ? [[-1, 0], [0, -1], [1, -1], [-1, 1], [0, 1], [1, 0]] : [[-1, -1], [0, -1], [1, 0], [-1, 1], [0, 1], [1, -1]]; neighborPositions.forEach(pos => { let newCol = col + pos[0]; let newRow = gridRow + pos[1]; if (newCol >= 0 && newCol < cols && newRow >= 0 && newRow < rows) { count += grid[newCol][newRow]; } }); return count; }
This function calculates the number of living neighbors for a given cell in a hexagonal grid. It first determines the relative positions of neighboring cells, which differ based on the row being even or odd (due to the staggered nature of the hexagonal grid). It then iterates through these positions, checking each neighboring cell’s state and counting how many are alive. Implementation
- Hexagonal Grid: The simulation uses a hexagonal grid, offering a twist to the traditional square cells, which influences the pattern formations and interactions.
- Ruleset Alternation: The game alternates between two rulesets based on the number of iterations. This variation introduces new dynamics and allows the bacterial colonies to regrow and continue the cycle.
- Color Dynamics: To enhance visual interest, each cell changes color depending on its state, adding a layer of visual complexity to the simulation.
Challenges
- Hexagonal Logic: Implementing the Game of Life on a hexagonal grid presented unique challenges, particularly in calculating neighbors and defining rules specific to the hexagonal arrangement.
- Ruleset Implementation: Crafting the logic for alternating rulesets while maintaining a coherent and continuous simulation required alot of trial and error.
Future Improvements
- User-Controlled Variables: Allowing users to adjust parameters like the iteration count for ruleset changes or the rules themselves could offer a more interactive and customizable experience.
- Advanced Interactions: Implementing additional interactions, such as dragging to create multiple cells or using gestures to influence the simulation, could enhance user engagement.
- Enhanced Aesthetics: Further refinement of the visual elements, such as gradient color transitions or adding background animations, could make the simulation even more captivating.