Skip to contents

A complete Snake game built entirely in R: arrow keys steer the snake, eating food grows it, hitting a wall or its own body resets the game. It demonstrates keyboard input via get_key_pressed() and the key enum, grid-based collision detection using R lists, draw_rectangle_v() for rendering the board, and set_target_fps() to control game speed.

Try it

Click the canvas and use the arrow keys to steer the snake.

Run

demo("snake", package = "raylibr")

Source code

library(raylibr)

width <- 20
height <- 16
scale <- 30

init_window(width * scale, height * scale, title = "R & Raylib: Snake")

snake <- list(as.integer(c(width, height) / 2))

walls <- c(lapply(1:width,  function(x) c(x, 1)),
           lapply(1:width,  function(x) c(x, height)),
           lapply(1:height, function(x) c(1, x)),
           lapply(1:height, function(x) c(width, x)))

`%on%` <- function(needle, haystack) {
  for (candidate in haystack) {
    if (all(candidate == needle)) return(TRUE)
  }
  FALSE
}

random_empty_cell <- function(w, h, nonempty) {
  pos <- c(sample(w, 1), sample(h, 1))
  while(pos %on% nonempty) pos <- c(sample(w, 1), sample(h, 1))
  pos
}

food <- random_empty_cell(width, height, c(walls, snake))

directions <- list(up = c(0, -1),
                   down = c(0, 1),
                   left = c(-1, 0),
                   right = c(1, 0))

direction <- c(0, 0)

set_target_fps(10)

while(!window_should_close()) {
  key_code <- get_key_pressed()
  key_name <- ifelse(key_code, names(key)[[match(key_code, key)]], NA)

  # Is our move valid?
  if (key_name %in% names(directions)) {
    new_direction <- directions[[key_name]]
    if (sum(abs(direction + new_direction)) > 0) {
      direction <- new_direction
    }
  }

  snake <- unique(snake)
  new_head <- snake[[1]] + direction

  if (new_head %on% c(walls, snake[-1])) {
    # ouch, restart
    direction <- c(0, 0)
    snake <- list(as.integer(c(width, height) / 2))
    food <- random_empty_cell(width, height, c(walls, snake))
  } else if (new_head %on% list(food)) {
    # grow
    snake <- c(list(new_head), snake)
    food <- random_empty_cell(width, height, c(walls, snake))
  } else {
    # just move
    snake <- c(list(new_head), head(snake, -1))
  }

  begin_drawing()
  clear_background("black")
  lapply(walls, function(x) { draw_rectangle_v((x - 1) * scale + 1, c(scale, scale) - 2, "sienna") } )
  lapply(snake, function(x) { draw_rectangle_v((x - 1) * scale + 1, c(scale, scale) - 2, "olivedrab") })
  draw_circle_v(food * scale - scale / 2, scale / 2 - 4, "red")
  draw_text(paste0("SCORE: ", length(snake) - 1), 4, 2, scale - 2, "white")
  end_drawing()
}

close_window()