--
-- Lua mutable playfield. Extracted from Beturing sources
-- and converted to a more conventional Lua OO style.
--
-- SPDX-FileCopyrightText: In 2012, Chris Pressey, the original author of this work, placed it into the public domain.
-- For more information, please refer to <https://unlicense.org/>
-- SPDX-License-Identifier: Unlicense
--
--
-- to use: ensure LUA_PATH has "thisdir/?.lua", then
--
-- local Playfield = require "playfield.lua"
-- p = Playfield.new()
--
-- to run demo standalone:
--
-- lua -i playfield.lua
-- Playfield_demo()
--
local Playfield = {}
--
-- Private function: pick the appropriate quadrant & translate
--
local pick_quadrant = function(self, x, y)
if x > 0 and y > 0 then return self.se, x, y end
if x > 0 and y <= 0 then return self.ne, x, 1-y end
if x <= 0 and y > 0 then return self.sw, 1-x, y end
if x <= 0 and y <= 0 then return self.nw, 1-x, 1-y end
end
--
-- Read the symbol at a given position in the playfield
--
function Playfield:peek(x, y)
local contents, nx, ny = pick_quadrant(self, x, y)
contents[ny] = contents[ny] or {} -- make sure row exists
local sym = contents[ny][nx] or " "
return sym
end
--
-- Write a symbol at a given position in the playfield
--
function Playfield:poke(x, y, sym)
local contents, nx, ny = pick_quadrant(self, x, y)
contents[ny] = contents[ny] or {} -- make sure row exists
contents[ny][nx] = sym
if not self.min_x or x < self.min_x then self.min_x = x end
if not self.max_x or x > self.max_x then self.max_x = x end
if not self.min_y or y < self.min_y then self.min_y = y end
if not self.max_y or y > self.max_y then self.max_y = y end
end
--
-- Return a string representing the playfield.
--
function Playfield:render(wrapper, start_x, start_y, end_x, end_y)
wrapper = wrapper or function(v) return v end
start_x = start_x or self.min_x
start_y = start_y or self.min_y
end_x = end_x or self.max_x
end_y = end_y or self.max_y
local y = start_y
local s = ""
while y <= end_y do
local x = start_x
while x <= end_x do
s = s .. wrapper(self:peek(x, y), x, y)
x = x + 1
end
s = s .. "\n"
y = y + 1
end
return s
end
--
-- Load a multi-line string into a Playfield.
--
function Playfield:load(program_text, transformer, sx, sy)
local i = 1
local x = sx or 0
local y = sy or 0
local len = string.len(program_text)
local c
while i <= len do
c = program_text:sub(i, i)
if c == "\n" then
x = 0
y = y + 1
else
c = transformer(c, x, y)
if c ~= nil then
self:poke(x, y, c)
end
x = x + 1
end
i = i + 1
end
end
Playfield.new = function()
local self = {
nw = {},
ne = {},
sw = {},
se = {},
min_x = nil,
min_y = nil,
max_x = nil,
max_y = nil
}
setmetatable(self, {__index = Playfield})
return self
end
Playfield_demo = function()
p = Playfield.new()
p:poke(0, 0, "*")
p:poke(1, 0, "/")
p:poke(1, 1, ">")
p:poke(0, 1, "v")
print(p:render())
end
return Playfield