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