Skip to content

Instantly share code, notes, and snippets.

@kirkegaard
Created September 10, 2025 15:17
Show Gist options
  • Save kirkegaard/335fcd19fea3a6e06fd6cdcce81c49e4 to your computer and use it in GitHub Desktop.
Save kirkegaard/335fcd19fea3a6e06fd6cdcce81c49e4 to your computer and use it in GitHub Desktop.
Hexagons
#include <math.h>
#include <proto/dos.h>
#include <proto/exec.h>
#include <proto/graphics.h>
#include <proto/intuition.h>
#include <stdint.h>
#include <stdlib.h>
#define SCREEN_WIDTH 320
#define SCREEN_HEIGHT 256
#define DEPTH 2
#define HEX_BASE 12 // base hex radius in pixels
#define SINLEN 1024 // sine table length (power of two)
#define FP_SCALE 1024 // fixed-point scale for sin/cos table
#define MAX_HEXAGONS 200 // max number of hexagons in grid
#define NOISE_SOURCES 12 // number of rotating noise activation points
struct Screen *screen = NULL;
struct Window *window = NULL;
struct RastPort *frontBuffer = NULL;
struct RastPort backBuffer;
struct BitMap *backBitMap = NULL;
static int16_t sin_table[SINLEN];
typedef struct {
int x, y;
int radius;
} HexCell;
static HexCell hex_grid[MAX_HEXAGONS];
static int hex_count = 0;
static int framecount = 0;
static int simple_noise(int x, int y, int z) {
int hash = ((x * 73) + (y * 137) + (z * 241)) & (SINLEN - 1);
return sin_table[hash];
}
static void init_sin_table(void) {
const double two_pi = 2.0 * M_PI;
for (int i = 0; i < SINLEN; ++i) {
double rad = (i * two_pi) / SINLEN;
sin_table[i] = (int16_t)round(sin(rad) * FP_SCALE);
}
}
static void init_hex_grid(void) {
const int r = HEX_BASE;
const int x_span = (r * 173) / 100;
const int margin = r * 2;
hex_count = 0;
BOOL row_offset = FALSE;
for (int y = margin; y <= SCREEN_HEIGHT - margin && hex_count < MAX_HEXAGONS;
y += (r * 3) / 2) {
int x_start = margin + (row_offset ? x_span / 2 : 0);
for (int x = x_start;
x <= SCREEN_WIDTH - margin && hex_count < MAX_HEXAGONS; x += x_span) {
// Center coordinates relative to screen center
hex_grid[hex_count].x = x - SCREEN_WIDTH / 2;
hex_grid[hex_count].y = y - SCREEN_HEIGHT / 2;
hex_grid[hex_count].radius = 0.0;
hex_count++;
}
row_offset = !row_offset;
}
}
static void clearBuffer(struct RastPort *rport) {
BltBitMap(rport->BitMap, 0, 0, rport->BitMap, 0, 0, SCREEN_WIDTH,
SCREEN_HEIGHT, 0x00, 0xFF, NULL);
}
static void drawHexagon(struct RastPort *rport, int cx, int cy, int r,
UBYTE color) {
int vx[6], vy[6];
SetAPen(rport, color);
for (int i = 0; i < 6; ++i) {
int angle_idx = ((i * SINLEN) / 6 + (SINLEN / 12)) & (SINLEN - 1);
int s = sin_table[angle_idx];
int c = sin_table[(angle_idx + (SINLEN / 4)) & (SINLEN - 1)];
vx[i] = cx + (r * c) / FP_SCALE;
vy[i] = cy + (r * s) / FP_SCALE;
}
Move(rport, vx[0], vy[0]);
for (int i = 1; i < 6; ++i) {
Draw(rport, vx[i], vy[i]);
}
Draw(rport, vx[0], vy[0]);
}
static void updateHexGrid(void) {
for (int i = 0; i < hex_count; i++) {
if (hex_grid[i].radius > 0) {
hex_grid[i].radius--;
}
}
int f = framecount / 2;
int noise_base_x = (simple_noise(f / 32, f, 0) * 100) / FP_SCALE;
int noise_base_y = (simple_noise(0, f, f / 16) * 100) / FP_SCALE;
for (int source = 0; source < NOISE_SOURCES; source++) {
int angle_deg = source * 30;
int angle_idx = (angle_deg * SINLEN) / 360;
int cos_val = sin_table[(angle_idx + (SINLEN / 4)) & (SINLEN - 1)];
int sin_val = sin_table[angle_idx];
// Position noise source in circle around base position
int source_x = noise_base_x + (cos_val * 20) / FP_SCALE;
int source_y = noise_base_y + (sin_val * 20) / FP_SCALE;
// Check which hexagons are within distance
for (int i = 0; i < hex_count; i++) {
int dx = hex_grid[i].x - source_x;
int dy = hex_grid[i].y - source_y;
int dist_sq = dx * dx + dy * dy;
// If within radius, increase radius
if (dist_sq < 30 * 30) {
hex_grid[i].radius += 1;
if (hex_grid[i].radius > HEX_BASE) {
hex_grid[i].radius = HEX_BASE;
}
}
}
}
}
static void drawHexGrid(struct RastPort *rport) {
for (int i = 0; i < hex_count; i++) {
int screen_x = hex_grid[i].x + SCREEN_WIDTH / 2;
int screen_y = hex_grid[i].y + SCREEN_HEIGHT / 2;
int radius = hex_grid[i].radius;
UBYTE color = (radius > 4) ? 1 : 2;
drawHexagon(rport, screen_x, screen_y, radius, color);
}
}
int main(void) {
// Open a screen
screen = OpenScreenTags(NULL, SA_Width, SCREEN_WIDTH, SA_Height,
SCREEN_HEIGHT, SA_Depth, DEPTH, SA_ShowTitle, FALSE,
SA_Quiet, TRUE, TAG_DONE);
if (!screen) {
return 1;
}
// Open a window on that screen; listen for RAWKEY and CLOSEWINDOW
window =
OpenWindowTags(NULL, WA_CustomScreen, (ULONG)screen, WA_Left, 0, WA_Top,
0, WA_Width, SCREEN_WIDTH, WA_Height, SCREEN_HEIGHT,
WA_Borderless, TRUE, WA_Backdrop, TRUE, WA_Activate, TRUE,
WA_IDCMP, IDCMP_RAWKEY | IDCMP_CLOSEWINDOW, TAG_DONE);
if (!window) {
CloseScreen(screen);
return 1;
}
// Initialize front and back buffers
frontBuffer = window->RPort;
backBitMap = AllocBitMap(SCREEN_WIDTH, SCREEN_HEIGHT, DEPTH, BMF_CLEAR,
screen->RastPort.BitMap);
if (!backBitMap) {
CloseWindow(window);
CloseScreen(screen);
return 1;
}
InitRastPort(&backBuffer);
backBuffer.BitMap = backBitMap;
// Set up color palette
SetRGB4(&screen->ViewPort, 0, 0, 0, 0);
SetRGB4(&screen->ViewPort, 1, 15, 8, 0);
SetRGB4(&screen->ViewPort, 2, 0, 15, 15);
// SetRGB4(&screen->ViewPort, 3, 15, 8, 0);
// Precompute sin table and initialize hex grid
init_sin_table();
init_hex_grid();
BOOL running = TRUE;
int frame = 0;
while (running) {
struct IntuiMessage *msg;
while ((msg = (struct IntuiMessage *)GetMsg(window->UserPort))) {
if (msg->Class == IDCMP_RAWKEY && msg->Code == 0x45) {
running = FALSE;
}
ReplyMsg((struct Message *)msg);
}
clearBuffer(&backBuffer);
updateHexGrid();
drawHexGrid(&backBuffer);
// Copy back buffer to front buffer
BltBitMap(backBitMap, 0, 0, frontBuffer->BitMap, 0, 0, SCREEN_WIDTH,
SCREEN_HEIGHT, 0xC0, 0xFF, NULL);
WaitBlit();
WaitTOF();
framecount++;
}
if (backBitMap) {
FreeBitMap(backBitMap);
}
CloseWindow(window);
CloseScreen(screen);
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment