add game of life to core examples
This commit is contained in:
parent
1f704be4e4
commit
3a59d7c143
282
examples/core/core_game_of_life.c
Normal file
282
examples/core/core_game_of_life.c
Normal 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
Normal file
BIN
examples/core/core_game_of_life.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 6.9 KiB |
Loading…
Reference in New Issue
Block a user