← how-to

snake

providers / raylib

program
load core::c::*;
load core::random;
load provider::raylib;

val CELL: int = 20;
val COLS: int = 40;
val ROWS: int = 30;
val WIDTH: int = 800;
val HEIGHT: int = 600;
val FPS: int = 60;
val STEP: float = 0.11;
val SEED: int = 0x5EED;

val KEY_RIGHT: int = 262;
val KEY_LEFT: int = 263;
val KEY_DOWN: int = 264;
val KEY_UP: int = 265;
val KEY_ENTER: int = 257;

val COLOR_BG: int = 0xFF1A1A1A;
val COLOR_SNAKE: int = 0xFF3EFDCC;
val COLOR_HEAD: int = 0xFF7CFFE4;
val COLOR_FOOD: int = 0xFF4D4DFF;
val COLOR_TEXT: int = 0xFFFFFFFF;

struct Cell {
  x: int,
  y: int,
}

fun spawn_snake() -> Vec<Cell> {
  mut body: Vec<Cell> = Vec::new();

  body.push(Cell { x = 18, y = 15 });
  body.push(Cell { x = 19, y = 15 });
  body.push(Cell { x = 20, y = 15 });

  body
}

fun hits_body(body: Vec<Cell>, x: int, y: int) -> bool {
  for i := 0..body.len() {
    match body.get(i) {
      Option::Some(cell) => {
        if cell.x == x && cell.y == y {
          return true;
        }
      }
      Option::None => {}
    }
  }

  false
}

fun head_of(body: Vec<Cell>) -> Cell {
  match body.get(body.len() - 1) {
    Option::Some(cell) => cell,
    Option::None => Cell { x = 0, y = 0 },
  }
}

fun main() {
  raylib::init_window(WIDTH, HEIGHT, CStr::new("zo snake"));
  raylib::set_target_fps(FPS);

  mut rng: Rng = Rng::new(SEED);
  mut body: Vec<Cell> = spawn_snake();
  mut dir_x: int = 1;
  mut dir_y: int = 0;
  mut food: Cell = Cell { x = 0, y = 0 };
  mut need_food: bool = true;
  mut timer: float = 0.0;
  mut score: int = 0;
  mut game_over: bool = false;

  loop {
    if raylib::window_should_close() { break }

    imu dt: float = raylib::get_frame_time();

    while need_food {
      imu fx: int = rng.range(0, COLS);
      imu fy: int = rng.range(0, ROWS);

      if !hits_body(body, fx, fy) {
        food = Cell { x = fx, y = fy };
        need_food = false;
      }
    }

    if !game_over {
      if raylib::is_key_pressed(KEY_UP) && dir_y == 0 {
        dir_x = 0;
        dir_y = -1;
      }

      if raylib::is_key_pressed(KEY_DOWN) && dir_y == 0 {
        dir_x = 0;
        dir_y = 1;
      }

      if raylib::is_key_pressed(KEY_LEFT) && dir_x == 0 {
        dir_x = -1;
        dir_y = 0;
      }

      if raylib::is_key_pressed(KEY_RIGHT) && dir_x == 0 {
        dir_x = 1;
        dir_y = 0;
      }

      timer += dt;

      if timer >= STEP {
        timer -= STEP;

        imu head: Cell = head_of(body);
        imu nx: int = head.x + dir_x;
        imu ny: int = head.y + dir_y;
        imu off_board: bool = nx < 0 || nx >= COLS || ny < 0 || ny >= ROWS;

        if off_board || hits_body(body, nx, ny) {
          game_over = true;
        } else {
          body.push(Cell { x = nx, y = ny });

          if nx == food.x && ny == food.y {
            score += 1;
            need_food = true;
          } else {
            body.remove(0);
          }
        }
      }
    } else if raylib::is_key_pressed(KEY_ENTER) {
      body.free();
      body = spawn_snake();
      dir_x = 1;
      dir_y = 0;
      score = 0;
      need_food = true;
      game_over = false;
    }

    raylib::begin_drawing();
      raylib::clear_background(COLOR_BG);

      raylib::draw_rectangle(
        food.x * CELL,
        food.y * CELL,
        CELL,
        CELL,
        COLOR_FOOD,
      );

      for i := 0..body.len() {
        match body.get(i) {
          Option::Some(cell) => {
            imu color: int = when i == body.len() - 1
              ? COLOR_HEAD
              : COLOR_SNAKE;

            raylib::draw_rectangle(
              cell.x * CELL + 1,
              cell.y * CELL + 1,
              CELL - 2,
              CELL - 2,
              color,
            );
          }
          Option::None => {}
        }
      }

      imu label: CStr = CStr::new("score " ++ score.to_str());

      raylib::draw_text(label, 8, 8, 20, COLOR_TEXT);

      if game_over {
        raylib::draw_text(CStr::new("GAME OVER"), 290, 270, 40, COLOR_FOOD);
        raylib::draw_text(CStr::new("PRESS ENTER"), 314, 322, 20, COLOR_TEXT);
      }
    raylib::end_drawing();
  }

  body.free();
  raylib::close_window();
}

reachout

echo -n 'dGhlQGNvbXBpbG9yZHMuaG91c2U=' | base64 --decode

For humans: faq.

For Ai agents: llms.txt (curated index) and llms-full.txt (full docs).

Privacy: No cookies, no ads, no tracking. It's like you were never here.