Browse Source

add game of life to core examples

pull/4597/head
Pacatro 5 days ago
parent
commit
3a59d7c143
2 changed files with 282 additions and 0 deletions
  1. +282
    -0
      examples/core/core_game_of_life.c
  2. BIN
      examples/core/core_game_of_life.png

+ 282
- 0
examples/core/core_game_of_life.c View File

@ -0,0 +1,282 @@
/*******************************************************************************************
*
* raylib [core] example - Game of life
*
*
* Example originally created with raylib 5.5
*
* Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
* BSD-like license that allows static linking with closed source software
*
* Copyright (c) 2024 Paco Algar Muñoz (@P4k02)
*
********************************************************************************************/
/*******************************************************************************************
*
* Controls:
* - Right Mouse Button: Drag to move the camera
* - Left Mouse Button: Toggle cell state in Draw mode
* - Mouse Wheel: Zoom in/out
* - Space Key: Toggle between Play/Draw mode (only works if cells are alive)
* - UP Arrow: Increase generations interval
* - DOWN Arrow: Decrease generations interval
* - R Key: Reset the grid
*
*******************************************************************************************/
#include <string.h> // Required for: memcpy()
#include "raylib.h"
#include "raymath.h" // Required for: Vector2Add(), Vector2Scale()
//------------------------------------------------------------------------------------------
// Constants Definition
//------------------------------------------------------------------------------------------
#define SCREEN_HEIGHT 900
#define SCREEN_WIDTH 1000
#define INITIAL_CAMERA_ZOOM 1.0f
#define ZOOM_SCALE 0.25f
#define BOARD_SPACING 50
#define BOARD_ROWS 100
#define BOARD_COLS 100
#define INIT_INTERVAL 0.2f
#define MAX_GENERATIONS 500
//------------------------------------------------------------------------------------------
// Types and Structures Definition
//------------------------------------------------------------------------------------------
typedef struct {
int isAlive;
Vector2 pos;
Vector2 size;
} Cell;
//----------------------------------------------------------------------------------
// Global Variables Definition
//----------------------------------------------------------------------------------
static Cell board[BOARD_ROWS][BOARD_COLS];
static int aliveCells = 0;
static int generations = 0;
static int playMode = 0;
//----------------------------------------------------------------------------------
// Module Functions Declaration
//----------------------------------------------------------------------------------
static void InitGrid(void);
static void DrawBoard(void);
static void DrawCells(void);
static void UpdateBoard(void);
static int CountAliveNeighbors(int x, int y);
static void ToggleCells(int x, int y);
static void NextGeneration(double *lastGenerationTime, float *generationInterval);
//------------------------------------------------------------------------------------
// Program main entry point
//------------------------------------------------------------------------------------
int main(int argc, char **argv) {
InitWindow(SCREEN_WIDTH, SCREEN_HEIGHT, "raylib [core] example - game of live");
SetTargetFPS(60); // Set our game to run at 60 frames-per-second
// Camera initialization
Camera2D camera = { 0 };
camera.zoom = INITIAL_CAMERA_ZOOM;
camera.offset = (Vector2){ SCREEN_WIDTH / 2.0f, SCREEN_HEIGHT / 2.0f };
camera.target = (Vector2){ (BOARD_COLS * BOARD_SPACING) / 2.0f, (BOARD_ROWS * BOARD_SPACING) / 2.0f };
double lastGenerationTime = 0.0;
float generations_interval = INIT_INTERVAL;
InitGrid();
// Main game loop
while (!WindowShouldClose()) {
if (aliveCells == 0) {
playMode = 0;
generations = 0;
}
// Toggle play/draw mode with space key
if (IsKeyPressed(KEY_SPACE) && aliveCells > 0 && generations < MAX_GENERATIONS) playMode = !playMode;
// Move camera with right mouse button
if (IsMouseButtonDown(MOUSE_BUTTON_RIGHT)) {
Vector2 delta = GetMouseDelta();
delta = Vector2Scale(delta, -INITIAL_CAMERA_ZOOM / camera.zoom);
camera.target = Vector2Add(camera.target, delta);
}
// Zoom in/out with mouse wheel
float wheel = GetMouseWheelMove();
if (wheel) {
Vector2 mousePos = GetScreenToWorld2D(GetMousePosition(), camera);
camera.offset = GetMousePosition();
camera.target = mousePos;
float scaleFactor = INITIAL_CAMERA_ZOOM + (ZOOM_SCALE * fabsf(wheel));
if (wheel < 0) scaleFactor = INITIAL_CAMERA_ZOOM / scaleFactor;
camera.zoom = Clamp(camera.zoom * scaleFactor, 0.125f, 64.0f);
}
// Reset all with R key
if (IsKeyPressed(KEY_R)) {
playMode = 0;
generations = 0;
aliveCells = 0;
generations_interval = INIT_INTERVAL;
InitGrid();
}
// Increase generations interval with UP arrow
if (IsKeyPressed(KEY_UP)) {
generations_interval += 0.1f;
if (generations_interval > MAX_GENERATIONS) generations_interval = MAX_GENERATIONS;
}
// Decrease generations interval with DOWN arrow
if (IsKeyPressed(KEY_DOWN)) {
generations_interval -= 0.1f;
if (generations_interval < 0.0) generations_interval = 0.0;
}
// Draw mode actions with left mouse button
if (!playMode && IsMouseButtonPressed(MOUSE_BUTTON_LEFT)) {
Vector2 mousePos = GetScreenToWorld2D(GetMousePosition(), camera);
int boardX = (int)(mousePos.x / BOARD_SPACING);
int boardY = (int)(mousePos.y / BOARD_SPACING);
ToggleCells(boardX, boardY);
}
if (!playMode && IsKeyPressed(KEY_SPACE))
NextGeneration(&lastGenerationTime, &generations_interval);
// Start generations with space key
if (playMode && aliveCells > 0) {
NextGeneration(&lastGenerationTime, &generations_interval);
if (generations >= MAX_GENERATIONS)
playMode = 0;
}
BeginDrawing();
ClearBackground(BLACK);
DrawRectangle(5, 5, 300, 110, Fade(RAYWHITE, 0.8f));
DrawText(playMode ? "Play mode" : "Draw mode", 10, 10, 20, BLACK);
DrawText(TextFormat("Generation: %d (Max: %d)", generations, MAX_GENERATIONS), 10, 35, 20, BLACK);
DrawText(TextFormat("Cells: %d", aliveCells), 10, 60, 20, BLACK);
DrawText(TextFormat("Generation interval: %.1fs", generations_interval), 10, 85, 20, BLACK);
BeginMode2D(camera);
DrawBoard();
DrawCells();
EndMode2D();
EndDrawing();
}
CloseWindow();
return 0;
}
//----------------------------------------------------------------------------------
// Module Functions Definition
//----------------------------------------------------------------------------------
static void InitGrid(void) {
for (int i = 0; i < BOARD_ROWS; i++) {
for (int j = 0; j < BOARD_COLS; j++) {
board[i][j].isAlive = 0;
board[i][j].pos.x = j * BOARD_SPACING;
board[i][j].pos.y = i * BOARD_SPACING;
board[i][j].size.x = BOARD_SPACING;
board[i][j].size.y = BOARD_SPACING;
}
}
}
static void DrawBoard(void) {
for (int i = 0; i <= BOARD_ROWS; i++) {
Vector2 start = {0, i * BOARD_SPACING};
Vector2 end = {BOARD_COLS * BOARD_SPACING, i * BOARD_SPACING};
DrawLineV(start, end, GRAY);
}
for (int j = 0; j <= BOARD_COLS; j++) {
Vector2 start = {j * BOARD_SPACING, 0};
Vector2 end = {j * BOARD_SPACING, BOARD_ROWS * BOARD_SPACING};
DrawLineV(start, end, GRAY);
}
}
static int CountAliveNeighbors(int x, int y) {
int count = 0;
// This is going to check the 8 surrounding cells
// If any of them are alive, then the cell is alive
for (int dy = -1; dy <= 1; dy++) {
for (int dx = -1; dx <= 1; dx++) {
if (dx == 0 && dy == 0) continue;
// Calculate the neighbor's coordinates
int nx = (x + dx + BOARD_COLS) % BOARD_COLS;
int ny = (y + dy + BOARD_ROWS) % BOARD_ROWS;
count += board[ny][nx].isAlive;
}
}
return count;
}
static void ToggleCells(int x, int y) {
if (x >= 0 && x < BOARD_COLS && y >= 0 && y < BOARD_ROWS) {
board[y][x].isAlive = !board[y][x].isAlive;
aliveCells += (board[y][x].isAlive ? 1 : -1);
}
}
static void UpdateBoard(void) {
Cell nextGrid[BOARD_ROWS][BOARD_COLS];
int newAliveCells = 0;
for (int i = 0; i < BOARD_ROWS; i++) {
for (int j = 0; j < BOARD_COLS; j++) {
int aliveNeighbors = CountAliveNeighbors(j, i);
nextGrid[i][j] = board[i][j];
nextGrid[i][j].isAlive = (board[i][j].isAlive && (aliveNeighbors == 2 || aliveNeighbors == 3)) ||
(!board[i][j].isAlive && aliveNeighbors == 3);
if (nextGrid[i][j].isAlive) newAliveCells++;
}
}
memcpy(board, nextGrid, sizeof(board));
aliveCells = newAliveCells;
}
static void DrawCells(void) {
for(int i = 0; i < BOARD_ROWS; i++) {
for(int j = 0; j < BOARD_COLS; j++) {
if (board[i][j].isAlive) {
DrawRectangleV(board[i][j].pos, board[i][j].size, WHITE);
DrawRectangleLinesEx(
(Rectangle) {
board[i][j].pos.x,
board[i][j].pos.y,
board[i][j].size.x,
board[i][j].size.y
},
1,
BLACK
);
}
}
}
}
static void NextGeneration(double *lastGenerationTime, float *generations_interval) {
double time = GetTime();
if (time - *lastGenerationTime >= *generations_interval) {
UpdateBoard();
generations++;
*lastGenerationTime = time;
}
}

BIN
examples/core/core_game_of_life.png View File

Before After
Width: 800  |  Height: 450  |  Size: 6.9 KiB

Loading…
Cancel
Save