#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <locale.h>
#include <signal.h>
int W = 120;
int H = 40;
const char *shades[] = {" ", "\u2591", "\u2592", "\u2593", "\u2588"};
void cleanup(int sig)
{
  (void)sig;
  printf("\x1b[0m");
  printf("\x1b[?25h");
  printf("\x1b[?1049l");
  fflush(stdout);
  exit(0);
}
void get_term_size(int *w, int *h)
{
  struct winsize ws;
  ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws);
  *w = ws.ws_col;
  *h = ws.ws_row;
}
int clamp(int x)
{
  if (x < 0) return 0;
  if (x > 255) return 255;
  return x;
}
float field(float x, float y, float t)
{
  float v = 0.0f;
  v += sinf(x * 2.1f + t * 0.9f);
  v += sinf(y * 1.7f - t * 1.1f);
  v += sinf((x + y) * 1.3f + t * 0.7f);
  v += sinf(sqrtf(x*x + y*y) * 6.0f - t * 2.0f);
  float ridge = sinf(x * 3.0f + y * 2.5f + t * 0.6f);
  v += fabsf(ridge) * 0.5f;
  v += sinf(x * 4.7f - y * 5.3f + t * 1.2f) * 0.35f;
  v += cosf(x * 5.1f + y * 3.9f - t * 0.9f) * 0.35f;
  float r = sqrtf(x*x + y*y);
  v += sinf(r * 14.0f - t * 4.0f) * 0.25f;
  for (int i = 0; i < 6; i++)
  {
    float fi = (float)i;
    float cx = sinf(t * (0.25f + fi * 0.07f)) * 1.0f;
    float cy = cosf(t * (0.18f + fi * 0.05f)) * 0.7f;
    float dx = x - cx;
    float dy = y - cy;
    float d = sqrtf(dx*dx + dy*dy);
    v += 0.5f / (1.0f + d * 5.0f);
    v += sinf(d * 8.0f - t * 1.5f) * 0.2f;
  }
  return v;
}
float terrain(float x, float z, float t)
{
  return tanhf(field(x, z, t) * 0.18f) * 1.0f;
}
void palette(float v, float t, int *r, int *g, int *b)
{
  float h = v * 0.8f + t * 0.3f;
  *r = clamp((int)(128.0f + 127.0f * sinf(h)));
  *g = clamp((int)(128.0f + 127.0f * sinf(h * 1.1f + 2.094f)));
  *b = clamp((int)(128.0f + 127.0f * sinf(h * 0.9f + 4.189f)));
}
int main()
{
  int oldW = 0;
  int oldH = 0;
  float t = 0.0f;
  size_t buf_size = 8 * 1024 * 1024;
  char *buf;
  setlocale(LC_ALL, "");
  signal(SIGINT, cleanup);
  signal(SIGTERM, cleanup);
  printf("\x1b[?1049h");
  printf("\x1b[?25l");
  printf("\x1b[2J");
  fflush(stdout);
  get_term_size(&W, &H);
  printf("\x1b[2J");
  printf("\x1b[%d;%dH", H / 2, (W - 29) / 2);
  printf("\x1b[1;38;2;255;200;100mhacked by recursion in 2026.\x1b[0m");
  fflush(stdout);
  usleep(2000000);
  buf = malloc(buf_size);
  if (!buf)
  {
    fprintf(stderr, "buffer alloc failed\n");
    return 1;
  }
  while (1)
  {
    int renderH;
    char *p;
    int last_r, last_g, last_b;
    int last_br, last_bg, last_bb;
    get_term_size(&W, &H);
    renderH = H * 2;
    if (W != oldW || H != oldH)
    {
      printf("\x1b[2J");
      oldW = W;
      oldH = H;
    }
    p = buf;
    p += sprintf(p, "\x1b[0m\x1b[1;1H");
    float aspect = (float)W / (float)renderH;
    float fov = 1.2f;
    float camAngle = t * 0.08f;
    float camDist = 2.8f;
    float camX = sinf(camAngle) * camDist;
    float camZ = cosf(camAngle) * camDist;
    float camY = 1.4f + sinf(t * 0.37f) * 0.25f;
    float lookDist = 1.6f;
    float lookX = sinf(camAngle + 0.35f) * lookDist;
    float lookZ = cosf(camAngle + 0.35f) * lookDist;
    float lookY = -0.7f + sinf(t * 0.17f) * 0.25f;
    float fwdX = lookX - camX;
    float fwdY = lookY - camY;
    float fwdZ = lookZ - camZ;
    float flen = sqrtf(fwdX * fwdX + fwdY * fwdY + fwdZ * fwdZ);
    fwdX /= flen; fwdY /= flen; fwdZ /= flen;
    float rightX = fwdY * 0.0f - fwdZ * 1.0f;
    float rightY = fwdZ * 0.0f - fwdX * 0.0f;
    float rightZ = fwdX * 1.0f - fwdY * 0.0f;
    float rlen = sqrtf(rightX * rightX + rightY * rightY + rightZ * rightZ);
    rightX /= rlen; rightY /= rlen; rightZ /= rlen;
    float upX = rightY * fwdZ - rightZ * fwdY;
    float upY = rightZ * fwdX - rightX * fwdZ;
    float upZ = rightX * fwdY - rightY * fwdX;
    last_r = -1; last_g = -1; last_b = -1;
    last_br = -1; last_bg = -1; last_bb = -1;
    for (int y = 0; y < renderH; y += 2)
    {
      p += sprintf(p, "\x1b[%d;1H", (y / 2) + 1);
      for (int x = 0; x < W - 1; x++)
      {
        float sx = ((float)x / (float)(W - 1) - 0.5f) * aspect * 2.0f * fov;
        float sy = (0.5f - (float)y / (float)renderH) * 2.0f * fov;
        float dx = fwdX + rightX * sx + upX * sy;
        float dy = fwdY + rightY * sx + upY * sy;
        float dz = fwdZ + rightZ * sx + upZ * sy;
        float dlen = sqrtf(dx * dx + dy * dy + dz * dz);
        dx /= dlen; dy /= dlen; dz /= dlen;
        int hit = 0;
        float hitX = 0, hitZ = 0;
        float hitD = 0;
        for (float d = 0.03f; d < 9.0f; d += 0.04f + d * 0.022f)
        {
          float px = camX + dx * d;
          float py = camY + dy * d;
          float pz = camZ + dz * d;
          float h = terrain(px, pz, t);
          if (py < h)
          {
            hit = 1;
            hitX = px; hitZ = pz;
            hitD = d;
            break;
          }
        }
        if (hit)
        {
          float eps = 0.006f;
          float hxp = terrain(hitX + eps, hitZ, t);
          float hxm = terrain(hitX - eps, hitZ, t);
          float hzp = terrain(hitX, hitZ + eps, t);
          float hzm = terrain(hitX, hitZ - eps, t);
          float nx = (hxm - hxp) / (2.0f * eps);
          float nz = (hzm - hzp) / (2.0f * eps);
          float ny = 1.0f;
          float nlen = sqrtf(nx * nx + ny * ny + nz * nz);
          nx /= nlen; ny /= nlen; nz /= nlen;
          float lx = 0.4f;
          float ly = 1.0f;
          float lz = 0.6f;
          float llen = sqrtf(lx * lx + ly * ly + lz * lz);
          lx /= llen; ly /= llen; lz /= llen;
          float diff = nx * lx + ny * ly + nz * lz;
          if (diff < 0) diff = 0;
          diff = diff * 0.55f + 0.45f;
          float depth = hitD / 9.0f;
          float fog = 1.0f - depth * depth * 0.7f;
          if (fog < 0.30f) fog = 0.30f;
          float shade = diff * fog;
          shade = shade * 0.55f + 0.45f;
          float v = field(hitX, hitZ, t);
          if (!isfinite(v)) v = 0;
          int r, g, b;
          palette(v, t, &r, &g, &b);
          int br = clamp(r / 2 + 15);
          int bg = clamp(g / 2 + 10);
          int bb = clamp(b / 2 + 25);
          if (r != last_r || g != last_g || b != last_b ||
              br != last_br || bg != last_bg || bb != last_bb)
          {
            p += sprintf(p, "\x1b[38;2;%d;%d;%d;48;2;%d;%d;%dm", r, g, b, br, bg, bb);
            last_r = r; last_g = g; last_b = b;
            last_br = br; last_bg = bg; last_bb = bb;
          }
          int si = (int)(shade * 4.99f);
          if (si < 0) si = 0;
          if (si > 4) si = 4;
          const char *c = shades[si];
          while (*c) *p++ = *c++;
        }
        else
        {
          float hLen = sqrtf(dx*dx + dz*dz);
          float farD = 7.0f;
          float px = camX;
          float pz = camZ;
          if (hLen > 0.001f)
          {
            px = camX + dx / hLen * farD;
            pz = camZ + dz / hLen * farD;
          }
          float v = field(px, pz, t);
          if (!isfinite(v)) v = 0;
          int r, g, b;
          palette(v, t, &r, &g, &b);
          int br = clamp(r / 2 + 15);
          int bg = clamp(g / 2 + 10);
          int bb = clamp(b / 2 + 25);
          if (r != last_r || g != last_g || b != last_b ||
              br != last_br || bg != last_bg || bb != last_bb)
          {
            p += sprintf(p, "\x1b[38;2;%d;%d;%d;48;2;%d;%d;%dm", r, g, b, br, bg, bb);
            last_r = r; last_g = g; last_b = b;
            last_br = br; last_bg = bg; last_bb = bb;
          }
          const char *c = shades[1];
          while (*c) *p++ = *c++;
        }
      }
      p += sprintf(p, "\x1b[0m");
      last_r = -1; last_g = -1; last_b = -1;
      last_br = -1; last_bg = -1; last_bb = -1;
    }
    p += sprintf(p, "\x1b[0m");
    fwrite(buf, 1, p - buf, stdout);
    fflush(stdout);
    usleep(16000);
    t += 0.025f;
  }
  free(buf);
  cleanup(0);
  return 0;
}

