restore: clean roulette implementation with dynamic GPU size

This commit is contained in:
2026-05-05 18:43:27 -04:00
parent 3fd97848bb
commit 0415f307b6

View File

@@ -1,158 +1,71 @@
-- 3D rotating cube (temporary test - roulette code commented out below)
print("Scanning peripherals...")
for _, name in ipairs(peripheral.getNames()) do
print(name .. " = " .. peripheral.getType(name))
end
local tris = {
-- SOUTH
{ 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 1.0, 1.0, 0.0 },
{ 0.0, 0.0, 0.0, 1.0, 1.0, 0.0, 1.0, 0.0, 0.0 },
-- NORTH
{ 1.0, 0.0, 1.0, 1.0, 1.0, 1.0, 0.0, 1.0, 1.0 },
{ 1.0, 0.0, 1.0, 0.0, 1.0, 1.0, 0.0, 0.0, 1.0 },
-- EAST
{ 1.0, 0.0, 0.0, 1.0, 1.0, 0.0, 1.0, 1.0, 1.0 },
{ 1.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0, 0.0, 1.0 },
-- WEST
{ 0.0, 0.0, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0, 0.0 },
{ 0.0, 0.0, 1.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0 },
-- TOP
{ 0.0, 1.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0, 1.0 },
{ 0.0, 1.0, 0.0, 1.0, 1.0, 1.0, 1.0, 1.0, 0.0 },
-- BOTTOM
{ 1.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0 },
{ 1.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0 },
}
local gpu
for _, name in ipairs(peripheral.getNames()) do
local t = peripheral.getType(name)
if t and t:find("gpu") then
gpu = peripheral.wrap(name)
print("Found GPU: " .. name)
break
end
end
if not gpu then error("No GPU peripheral found.") end
gpu.refreshSize()
gpu.setSize(64)
local pw, ph, bx, by, sb = gpu.getSize()
print(("GPU pixel size: %sx%s blocks: %sx%s per-block: %s"):format(tostring(pw), tostring(ph), tostring(bx), tostring(by), tostring(sb)))
local gl = gpu.createWindow3D(1, 1, pw, ph)
gl.glFrustum(90, 0.1, 1000)
gl.glDirLight(0, 0, -1)
local rot = 0
while true do
gl.clear()
gl.glDisable(0xDE1)
gl.glTranslate(0, 1, 3)
gl.glRotate(rot, 0, 1, 0)
gl.glRotate(rot, 0, 0, 1)
rot = rot + 3
gl.glBegin()
for k, v in pairs(tris) do
local ci = math.floor((k - 1) / 2)
gl.glVertex(v[1], v[2], v[3])
local cv = 255
if ci % 2 == 0 then cv = 127 end
if math.floor(ci / 2) == 0 then
gl.glColor(cv, 0, 0)
elseif math.floor(ci / 2) == 1 then
gl.glColor(0, cv, 0)
else
gl.glColor(0, 0, cv)
end
gl.glVertex(v[4], v[5], v[6])
gl.glVertex(v[7], v[8], v[9])
end
gl.glEnd()
gl.render()
gl.sync()
gpu.sync()
sleep(0.01)
end
-- =============================================================================
-- ROULETTE CODE (commented out)
-- =============================================================================
--[[
-- Roulette Machine -- Roulette Machine
-- Designed for Tom's Peripherals GPU + screen blocks (multi-screen wall). -- Designed for Tom's Peripherals GPU + screen blocks (multi-screen wall).
-- Falls back to vanilla CC:Tweaked monitor if no GPU is attached.
-- --
-- Behaviour: -- Behaviour:
-- * On boot, sizes itself to the full screen surface. -- * On boot, discovers the GPU peripheral by scanning all attached peripherals.
-- * Calls refreshSize() + setSize(64) to bind the full monitor wall.
-- * Draws a red/black/green pocket ring around the perimeter. -- * Draws a red/black/green pocket ring around the perimeter.
-- * Waits for a redstone signal on any side, then spins the "ball" -- * Waits for a redstone signal on any side, then spins the "ball"
-- around the ring with random duration and ease-out deceleration, -- around the ring with random duration and ease-out deceleration,
-- finally landing on a uniformly random pocket. -- finally landing on a uniformly random pocket.
---------------------------------------------------------------------- ----------------------------------------------------------------------
-- Backend abstraction (GPU vs vanilla monitor) -- GPU discovery
---------------------------------------------------------------------- ----------------------------------------------------------------------
-- All drawing goes through a small `gfx` table with these methods:
-- gfx.size() -> width, height in cells/pixels
-- gfx.fillRect(x, y, w, h, col) -- col is a backend-specific color
-- gfx.text(x, y, str, fg, bg)
-- gfx.clear(col)
-- gfx.sync() -- present frame (no-op for monitor)
-- gfx.colors -- table with .red .black .green .white .bg
-- gfx.cellSize -- pixel size of one "pocket cell"
local gfx
local function findGPU() local function findGPU()
-- Tom's Peripherals registers GPUs as tm_gpu_0, tm_gpu_1, ... so print("[roulette] Scanning peripherals...")
-- scan all peripherals for any type that starts with "tm_gpu" or for _, name in ipairs(peripheral.getNames()) do
-- exposes the GPU API surface. local t = peripheral.getType(name)
for _, side in ipairs(peripheral.getNames()) do print(" " .. name .. " = " .. tostring(t))
local t = peripheral.getType(side) if t and t:find("gpu") then
if t and t:sub(1, 6) == "tm_gpu" then print("[roulette] Using GPU: " .. name)
return peripheral.wrap(side), t return peripheral.wrap(name)
end
end
for _, side in ipairs(peripheral.getNames()) do
local p = peripheral.wrap(side)
if p and p.refreshSize and p.filledRectangle and p.sync then
return p, peripheral.getType(side)
end end
end end
return nil return nil
end end
local function makeGpuBackend(gpu) ----------------------------------------------------------------------
-- refreshSize() schedules a server-thread monitor scan asynchronously. -- Backend
-- sleep(0) yields back to the server, giving it a tick to finish. ----------------------------------------------------------------------
local gfx
local function initBackend()
local gpu = findGPU()
if not gpu then
error("No GPU peripheral found. Attach a Tom's Peripherals GPU adjacent to the monitor wall.")
end
-- Let the server tick resolve the monitor wall geometry before setSize.
gpu.refreshSize() gpu.refreshSize()
sleep(0) sleep(0)
gpu.setSize(64) gpu.setSize(64)
-- getSize() -> pixelW, pixelH, blocksX, blocksY, sizePerBlock
local pw, ph, bx, by, sb = gpu.getSize() local pw, ph, bx, by, sb = gpu.getSize()
print(("[roulette] GPU pixel size: %sx%s blocks: %sx%s per-block: %s") print(("[roulette] GPU: %dx%d px | %dx%d blocks | %d px/block")
:format(tostring(pw), tostring(ph), tostring(bx), tostring(by), tostring(sb))) :format(pw, ph, bx or 0, by or 0, sb or 0))
if not pw or not ph or pw < 8 or ph < 8 then if not pw or pw < 8 or ph < 8 then
error(("GPU reports unusable pixel size %sx%s.") error(("GPU pixel size %dx%d is too small. Is the monitor wall placed and facing correctly?")
:format(tostring(pw), tostring(ph))) :format(pw or 0, ph or 0))
end end
-- Pick a cell size that gives at least 16x16 cells, capped at 16 px. -- Cell size: target at least 16 cells on the short axis, max 16 px each.
local cell = math.max(2, math.min(16, math.floor(math.min(pw, ph) / 16))) local cell = math.max(2, math.min(16, math.floor(math.min(pw, ph) / 16)))
print(("[roulette] Using cell size: %d px -> %dx%d cells"):format( local cw = math.floor(pw / cell)
cell, math.floor(pw / cell), math.floor(ph / cell))) local ch = math.floor(ph / cell)
print(("[roulette] Cell size: %d px -> grid: %dx%d cells"):format(cell, cw, ch))
return { return {
kind = "gpu", kind = "gpu",
cellSize = cell, cellSize = cell,
pixelW = pw, pixelW = pw,
pixelH = ph, pixelH = ph,
size = function() size = function() return cw, ch end,
return math.floor(pw / cell), math.floor(ph / cell)
end,
fillRect = function(x, y, w, h, col) fillRect = function(x, y, w, h, col)
-- 1-indexed pixel coords, per gpuDraw.lua example.
local px = (x - 1) * cell + 1 local px = (x - 1) * cell + 1
local py = (y - 1) * cell + 1 local py = (y - 1) * cell + 1
gpu.filledRectangle(px, py, w * cell, h * cell, col) gpu.filledRectangle(px, py, w * cell, h * cell, col)
@@ -160,15 +73,12 @@ local function makeGpuBackend(gpu)
text = function(x, y, str, fg, bg) text = function(x, y, str, fg, bg)
local px = (x - 1) * cell + 1 local px = (x - 1) * cell + 1
local py = (y - 1) * cell + 1 local py = (y - 1) * cell + 1
-- drawText(x, y, text, textColor, bgColor, size, padding) pcall(gpu.drawText, px, py, str, fg or 0xFFFFFF, bg or 0x000000, 1, 0)
pcall(gpu.drawText, px, py, str, fg, bg, 1, 0)
end, end,
clear = function(col) clear = function(col)
if col then gpu.fill(col) else gpu.fill() end if col then gpu.fill(col) else gpu.fill() end
end, end,
sync = function() sync = function() gpu.sync() end,
gpu.sync()
end,
colors = { colors = {
red = 0xE53935, red = 0xE53935,
black = 0x101010, black = 0x101010,
@@ -179,58 +89,12 @@ local function makeGpuBackend(gpu)
} }
end end
local function makeMonitorBackend()
local mon = peripheral.find("monitor")
if not mon then return nil end
mon.setTextScale(0.5)
return {
kind = "monitor",
cellSize = 1,
size = function() return mon.getSize() end,
fillRect = function(x, y, w, h, col)
mon.setBackgroundColor(col)
for dy = 0, h - 1 do
mon.setCursorPos(x, y + dy)
mon.write(string.rep(" ", w))
end
end,
text = function(x, y, str, fg, bg)
mon.setBackgroundColor(bg or colors.black)
mon.setTextColor(fg or colors.white)
mon.setCursorPos(x, y)
mon.write(str)
end,
clear = function(col)
mon.setBackgroundColor(col)
mon.clear()
end,
sync = function() end,
colors = {
red = colors.red,
black = colors.black,
green = colors.green,
white = colors.white,
bg = colors.black,
},
}
end
local function initBackend()
local gpu = findGPU()
if gpu then
return makeGpuBackend(gpu)
end
local mon = makeMonitorBackend()
if mon then return mon end
error("No GPU or monitor attached. Connect a Tom's Peripherals GPU + screens, or a CC monitor.")
end
---------------------------------------------------------------------- ----------------------------------------------------------------------
-- Wheel state -- Wheel state
---------------------------------------------------------------------- ----------------------------------------------------------------------
local W, H -- size in cells local W, H
local perimeter -- ordered list of {x, y, color, label} local perimeter
local ballIndex local ballIndex
local lastBallIndex local lastBallIndex
@@ -243,10 +107,10 @@ local function buildPerimeter()
perimeter = {} perimeter = {}
local function add(x, y) table.insert(perimeter, { x = x, y = y }) end local function add(x, y) table.insert(perimeter, { x = x, y = y }) end
for x = 1, W do add(x, 1) end -- top for x = 1, W do add(x, 1) end -- top L->R
for y = 2, H do add(W, y) end -- right for y = 2, H do add(W, y) end -- right T->B
for x = W - 1, 1, -1 do add(x, H) end -- bottom for x = W - 1, 1, -1 do add(x, H) end -- bottom R->L
for y = H - 1, 2, -1 do add(1, y) end -- left for y = H - 1, 2, -1 do add(1, y) end -- left B->T
for i, c in ipairs(perimeter) do for i, c in ipairs(perimeter) do
if i == 1 then if i == 1 then
@@ -271,7 +135,6 @@ local function drawWheel()
end end
local function drawCenter(lines) local function drawCenter(lines)
-- Clear interior
if W > 2 and H > 2 then if W > 2 and H > 2 then
gfx.fillRect(2, 2, W - 2, H - 2, gfx.colors.bg) gfx.fillRect(2, 2, W - 2, H - 2, gfx.colors.bg)
end end
@@ -296,7 +159,7 @@ local function moveBall(newIdx)
if lastBallIndex then repaintPocket(lastBallIndex) end if lastBallIndex then repaintPocket(lastBallIndex) end
drawBall(newIdx) drawBall(newIdx)
lastBallIndex = newIdx lastBallIndex = newIdx
ballIndex = newIdx ballIndex = newIdx
gfx.sync() gfx.sync()
end end
@@ -310,9 +173,9 @@ local function easeOut(t)
end end
local function spin() local function spin()
local n = #perimeter local n = #perimeter
local spinTime = SPIN_MIN_TIME + math.random() * (SPIN_MAX_TIME - SPIN_MIN_TIME) local spinTime = SPIN_MIN_TIME + math.random() * (SPIN_MAX_TIME - SPIN_MIN_TIME)
local elapsed = 0 local elapsed = 0
drawCenter({ "SPINNING" }) drawCenter({ "SPINNING" })
gfx.sync() gfx.sync()
@@ -320,24 +183,22 @@ local function spin()
drawCenter({ "" }) drawCenter({ "" })
while elapsed < spinTime do while elapsed < spinTime do
local t = elapsed / spinTime local t = elapsed / spinTime
local eased = easeOut(t) local eased = easeOut(t)
local delay = START_DELAY + (END_DELAY - START_DELAY) * eased local delay = START_DELAY + (END_DELAY - START_DELAY) * eased
local jump = math.max(1, math.floor((1 - eased) * 4 + 0.5)) local jump = math.max(1, math.floor((1 - eased) * 4 + 0.5))
local nextIdx = ((ballIndex - 1 + jump) % n) + 1 moveBall(((ballIndex - 1 + jump) % n) + 1)
moveBall(nextIdx)
sleep(delay) sleep(delay)
elapsed = elapsed + delay elapsed = elapsed + delay
end end
-- Final settle: pick a uniformly random pocket and crawl to it. -- Crawl to a uniformly random final pocket.
local finalIdx = math.random(1, n) local finalIdx = math.random(1, n)
local steps = ((finalIdx - ballIndex) % n) local steps = (finalIdx - ballIndex) % n
if steps == 0 then steps = n end if steps == 0 then steps = n end
for i = 1, steps do for i = 1, steps do
local nextIdx = ((ballIndex - 1 + 1) % n) + 1 moveBall(((ballIndex - 1 + 1) % n) + 1)
moveBall(nextIdx)
sleep(END_DELAY + i * 0.04) sleep(END_DELAY + i * 0.04)
end end
@@ -346,8 +207,8 @@ end
local function announce(pocket) local function announce(pocket)
local name = "BLACK" local name = "BLACK"
if pocket.color == gfx.colors.red then name = "RED" if pocket.color == gfx.colors.red then name = "RED" end
elseif pocket.color == gfx.colors.green then name = "GREEN" end if pocket.color == gfx.colors.green then name = "GREEN" end
drawCenter({ "WINNER", name .. " " .. pocket.label }) drawCenter({ "WINNER", name .. " " .. pocket.label })
gfx.sync() gfx.sync()
end end
@@ -365,7 +226,7 @@ local function start()
error(("Screen too small: %dx%d cells. Add more screen blocks."):format(W, H)) error(("Screen too small: %dx%d cells. Add more screen blocks."):format(W, H))
end end
buildPerimeter() buildPerimeter()
ballIndex = 1 ballIndex = 1
lastBallIndex = nil lastBallIndex = nil
drawWheel() drawWheel()
drawBall(ballIndex) drawBall(ballIndex)
@@ -401,5 +262,3 @@ local function main()
end end
return { start = start, stop = stop, main = main } return { start = start, stop = stop, main = main }
--]]