Snake

A starting point for a simple snake game on a 80x60 grid (640x480 px).

I was planning to use @sn[:trail] for collision detection.. but that went south and I added @map and the @mapper lambda for that.

Missing: no food, no points, no start- or exit screen.

Defaults right now to a 88 grid long snake.

Keys: arrow keys & ‘q’ for quit.

require 'ruby2d'

DIFFICULTY, SPEED, WRAP, GROW = nil, 30, true, 2

case DIFFICULTY
  when 1 then SPEED, WRAP, GROW = 30, true, 1
  when 2 then SPEED, WRAP, GROW = 40, true, 2
  when 3 then SPEED, WRAP, GROW = 50, true, 3
  when 4 then SPEED, WRAP, GROW = 55, false, 3
  when 5 then SPEED, WRAP, GROW = 60, false, 5
end

set fps_cap: SPEED
@mapper = ->(x,y, set) { @map[y/8][x/8] = (set ? '1' : '0') }
@map = [].tap {|ar| 60.times {ar << '0' * 80 } }
@mapper.(320, 240, true)

class Integer alias :btw :between? end
class Array alias : :include? end

class Pos
  attr_accessor :x, :y
  def initialize(x, y) = ( @x, @y = x, y )
end

@sn = { len: 88, trail: [Square.new(x: 320, y: 240, size: 8)],
        pos: Pos.new(40, 30), dir: Pos.new(8, 0) }

def grow_or_move(pos, grow: false)
  abort('oroboro') if @map[pos.y/8][pos.x/8] == '1'
  @sn[:trail] << (grow ? Square.new(size: 8) : @sn[:trail].shift)
  @mapper.(pos.x, pos.y, true)
  @mapper.(@sn[:trail].last.x, @sn[:trail].last.y, false) unless grow
  @sn[:trail].last.x, @sn[:trail].last.y = pos.x, pos.y
end

on :key_down do |e|
  if %w(left right).(e.key) && @sn[:dir].x.zero?
    @sn[:dir].x, @sn[:dir].y = (e.key == 'left') ? -8 : 8, 0
  elsif %w(up down).(e.key) && @sn[:dir].y.zero?
    @sn[:dir].y, @sn[:dir].x = (e.key == 'up') ? -8 : 8, 0
  else close if e.key == 'q'
  end
end

update do
  grow_or_move(Pos.new(@sn[:trail].last.x += @sn[:dir].x, @sn[:trail].last.y += @sn[:dir].y), grow: @sn[:trail].size <= @sn[:len])
  
  if WRAP
    @sn[:trail].last.x, @sn[:trail].last.y = @sn[:trail].last.x % 640, @sn[:trail].last.y % 480
  else abort('outside') unless @sn[:trail].last.x.btw(0, 640) && @sn[:trail].last.y.btw(0, 480)
  end
end

show