Pong Wars, MMO edition


This one is based on Pong Wars.

-- Pong wars with more than 2 colors and ability to speed up simulation.
local n = 20  -- px per square
local v = 20  -- px/s ball velocity
-- If v gets too big, collision simulations will have errors
-- and internal cells will start flipping that are behind the border.
-- Instead, just simulate multiple steps per frame to speed things up.
local simulation_speed = 20  -- number of steps per frame
local num_colors = 105
function hsl(h, s, l)
  if s<=0 then return {l,l,l} end
  h = h*6
  local c = (1-abs(2*l-1))*s
  local x = (1-abs(h%2-1))*c
  local m,r,g,b = (l-0.5*c), 0,0,0
  if h < 1 then r,g,b = c,x,0
  elseif h < 2 then r,g,b = x,c,0
  elseif h < 3 then r,g,b = 0,c,x
  elseif h < 4 then r,g,b = 0,x,c
  elseif h < 5 then r,g,b = x,0,c
  else r,g,b = c,0,x end
  return {r+m, g+m, b+m}
end
-- initialize a few colors
-- equidistant on the color wheel, but contrast between nearby colors
colors = {}
local d = 4
if num_colors == 2 then
  table.insert(colors, {0.8, 0.8, 1})
  table.insert(colors, {0.8, 0.9, 0.8})
elseif num_colors < d then
  for h=0, 1, 1/num_colors do
    table.insert(colors, hsl(h, 1, 0.8))
    if #colors == num_colors then break end
  end
else
  -- ensure ball contrast
  -- consecutive colors are always 1/d apart
  for start = 1, num_colors/d do
    for h = start/num_colors, 1, 1/d do
      table.insert(colors, hsl(h, 1, 0.8))
      if #colors == num_colors then break end
    end
    if #colors == num_colors then break end
  end
end
-- initialize a w*h board with one of those colors
local w = floor((Safe_width-60)/n)
local h = floor((Safe_height-Menu_bottom)/n)
local board = {}
for y=1,h do
  local row = {}
  for x=1,w do
    table.insert(row, rand(#colors))
  end
  table.insert(board, row)
end
local px = (Safe_width-n*w)/2
-- initialize one ball per color to random locations
balls = {}  -- map from color index to ball converting squares to that color (NOT ball of that color)
for i=1, #colors do
  table.insert(balls, {x=rand(w)*n, y=rand(h)*n})
end
-- give each ball a unit velocity in a random direction
function randNorm2()
  local p = {x=rand(-100,100), y=rand(-100,100)}
  local m = math.sqrt(p.x^2 + p.y^2)
  return {x=p.x/m, y=p.y/m}
end
for _,b in ipairs(balls) do
  b.v = randNorm2()
end
local frame_times = {sum=0}
function car.draw()
  for y=1,h do
    for x=1,w do
      color(unpack(colors[board[y][x]]))
      rect('fill', px+(x-1)*n, Menu_bottom+(y-1)*n, n,n)
    end
  end
  for i = 1,#colors do
    local next = i+1
    if next > #colors then next = 1 end
    color(unpack(colors[next]))
    circle('fill', px+balls[i].x, Menu_bottom+balls[i].y, n/2)
  end
  draw_hud()
end
function draw_hud()
  color(0.2,0.2,0.2)
  if simulation_speed == 1 then
    g.print(('%d frames/s'):format(#frame_times), 100,100)
    return
  end
  local sim_format = '%d'
  if simulation_speed >= 10000 then
    sim_format = '%0.1e'
  end
  g.print((sim_format..'x      %d frames/s'):format(simulation_speed, #frame_times), 100,100)
end
function car.update(dt)
  table.insert(frame_times, dt)
  frame_times.sum = frame_times.sum + dt
  while frame_times.sum > 1 do
    local t = table.remove(frame_times, 1)
    frame_times.sum = frame_times.sum - t
  end
  for i=1,simulation_speed do
    next_step(min(dt, 0.2))
  end
end
function next_step(dt)
  for c,b in ipairs(balls) do
    if b.v.x < 0 and (b.x < 1/2*n or maybe_set_board(b.x-1/2*n, b.y, c)) then
      b.v.x = abs(b.v.x)
    elseif b.v.x > 0 and (b.x > (w-1/2)*n or maybe_set_board(b.x+1/2*n, b.y, c)) then
      b.v.x = -abs(b.v.x)
    elseif b.v.y < 0 and (b.y < 1/2*n or maybe_set_board(b.x, b.y-1/2*n, c)) then
      b.v.y = abs(b.v.y)
    elseif b.v.y > 0 and (b.y > (h-1/2)*n or maybe_set_board(b.x, b.y+1/2*n, c)) then
      b.v.y = -abs(b.v.y)
    end
    b.x = b.x + v*b.v.x*dt
    b.y = b.y + v*b.v.y*dt
  end
end
function maybe_set_board(x, y, c)
  local x0, y0 = x, y
  x, y = s(x), s(y)
  if board[y] == nil then return end
  if board[y][x] == c then return end
  board[y][x] = c
  return true
end
function s(z) return 1+floor(z/n) end

If you try pasting any of these programs into Lua Carousel, remember to first run the abbreviations on one of the example screens. Or if you've deleted that screen, here are the abbreviations I used in this post:

g = love.graphics
rect = g.rectangle
circle = g.circle
color = g.setColor
min, max = math.min, math.max
floor, ceil = math.floor, math.ceil
abs, rand = math.abs, math.random

Get Lua Carousel

Leave a comment

Log in with itch.io to leave a comment.