Skip to content

Instantly share code, notes, and snippets.

@TwoSquirrels
Created January 30, 2025 03:59
Show Gist options
  • Save TwoSquirrels/103eef8469ead0d619ce002618be709b to your computer and use it in GitHub Desktop.
Save TwoSquirrels/103eef8469ead0d619ce002618be709b to your computer and use it in GitHub Desktop.
QR code generator for Computer Craft
-- SPDX-FileCopyrightText: 2025 TwoSquirrels
-- SPDX-License-Identifier: Apache-2.0
local qrencode = dofile("luaqrcode/qrencode.lua")
--- Generate a QR code matrix with padding.
--- @param text string the text to encode
--- @return boolean[][]|nil matrix the QR code matrix or nil if failed
local function qrcode(text)
local ok, tab = qrencode.qrcode(text)
if not ok then
return nil
end
local matrix = {}
for i = 1, #tab + 2 do
matrix[i] = {}
for j = 1, #tab + 2 do
matrix[i][j] = false
end
end
for i = 1, #tab do
for j = 1, #tab do
matrix[i + 1][j + 1] = tab[i][j] > 0
end
end
return matrix
end
--- Move the turtle to the specified direction.
--- @param left boolean whether to turn left
local function turn(left)
if left then
turtle.turnLeft()
else
turtle.turnRight()
end
end
--- Move the turtle forward, dig or attack if necessary.
--- @return boolean whether the turtle moved forward
local function forceForward()
if turtle.getFuelLevel() == 0 then
print("Error: out of fuel!")
return false
end
while not turtle.forward() do
if turtle.detect() then
turtle.dig()
else
turtle.attack()
end
end
return true
end
--- Elevate the turtle to the specified direction, dig or attack if necessary.
--- @param up boolean whether to elevate up
--- @return boolean whether the turtle elevated
local function forceElevate(up)
if turtle.getFuelLevel() == 0 then
print("Error: out of fuel!")
return false
end
if up then
while not turtle.up() do
if turtle.detectUp() then
turtle.digUp()
else
turtle.attackUp()
end
end
else
while not turtle.down() do
if turtle.detectDown() then
turtle.digDown()
else
turtle.attackDown()
end
end
end
return true
end
--- Place a block at the vertical position, dig or attack if necessary.
--- @param slot integer the slot number
--- @param up boolean whether to place up
local function forcePlaceVertical(slot, up)
turtle.select(slot)
if up then
while not turtle.placeUp() do
if turtle.detectUp() then
turtle.digUp()
else
turtle.attackUp()
end
end
else
while not turtle.placeDown() do
if turtle.detectDown() then
turtle.digDown()
else
turtle.attackDown()
end
end
end
end
--- Get the slot with the specified color.
--- @param black boolean whether to get the black slot (white: 1-8 slots, black: 9-16 slots)
--- @return integer|nil slot the slot number or nil if not found
local function getSlot(black)
local slot = black and 9 or 1
for _ = 1, 8 do
turtle.select(slot)
if turtle.getItemCount(slot) > 0 then
return slot
end
slot = slot + 1
end
return nil
end
--- Check if the turtle has enough fuel. Print an error message if not.
--- @param required integer the required fuel level
--- @return boolean whether the turtle has enough fuel
local function checkFuel(required)
if turtle.getFuelLevel() < required then
print("Error: not enough fuel! (required: " .. required .. ", stock: " .. turtle.getFuelLevel() .. ")")
return false
end
return true
end
--- Check if the turtle has enough blocks. Print an error message if not.
--- @param required { [1]: integer, [2]: integer } the required number of blocks ([1]: 1-8 slots, [2]: 9-16 slots)
--- @return boolean whether the turtle has enough blocks
local function checkStock(required)
local stock = {0, 0}
for i = 1, 8 do
stock[1] = stock[1] + turtle.getItemCount(i)
end
for i = 9, 16 do
stock[2] = stock[2] + turtle.getItemCount(i)
end
local shortage = false
if stock[1] < required[1] then
print("Error: not enough WHITE blocks! (required: " .. required[1] .. ", stock: " .. stock[1] .. ")")
shortage = true
end
if stock[2] < required[2] then
print("Error: not enough BLACK blocks! (required: " .. required[2] .. ", stock: " .. stock[2] .. ")")
shortage = true
end
return not shortage
end
--- @type table<string, { [1]: integer, [2]: integer }>
local DIRECTIONS = {
ul = {-1, -1},
ur = {-1, 1},
dl = {1, -1},
dr = {1, 1},
}
--- @type string[]
local args = {...}
if #args < 2 or #args > 3 or not DIRECTIONS[args[1]] then
print("Error: invalid arguments!")
print("")
print("Usage: qr [ul|ur|dl|dr] <text>")
print("")
print("Hint: Place WHITE blocks in the UPPER row and")
print(" BLACK blocks in the LOWER row of the inventory.")
print("")
return 1
end
-- generate the QR code
local matrix = qrcode(table.concat(args, " ", 2))
if not matrix then
print("Error: failed to generate QR code!")
return 1
end
print("QR code size: " .. #matrix .. "x" .. #matrix)
if #matrix > 31 then
print("Error: QR code is too large! (max 31x31)")
return 1
end
local qrDir = DIRECTIONS[args[1]]
local mi, mj = qrDir[1] >= 0 and 0 or #matrix + 1, qrDir[2] >= 0 and 1 or #matrix
local dmi, dmj = qrDir[1], qrDir[2]
-- check if the turtle has enough fuel and blocks
if not checkFuel(#matrix * #matrix) then
return 1
end
local requiredBlocks = {0, 0}
for i = 1, #matrix do
for j = 1, #matrix do
if matrix[i][j] then
requiredBlocks[2] = requiredBlocks[2] + 1
else
requiredBlocks[1] = requiredBlocks[1] + 1
end
end
end
if not checkStock(requiredBlocks) then
return 1
end
-- draw the QR code
print("Drawing QR code...")
for i = 1, #matrix do
turn(dmj < 0)
-- draw a row
for j = 1, #matrix do
if turtle.getFuelLevel() == 0 then
print("Error: out of fuel!")
return 1
end
if j == 1 then
forceElevate(dmi < 0)
mi = mi + dmi
else
forceForward()
mj = mj + dmj
end
local slot = getSlot(matrix[mi][mj])
if not slot then
print("Error: not enough blocks!")
return 1
end
forcePlaceVertical(slot, dmi >= 0)
end
dmj = -dmj
turn(dmj < 0)
print("Progress: " .. i .. "/" .. #matrix)
end
print("QR code drawn!")
-- return to the starting point
print("Returning to the starting point...")
dmi = -dmi
dmj = -dmj
turn(dmj < 0)
turn(dmj < 0)
forceForward()
for _ = 1, #matrix do
forceElevate(dmi < 0)
end
if #matrix % 2 == 1 then
turn(dmj < 0)
for _ = 1, #matrix - 1 do
forceForward()
end
end
print("Returned to the starting point! Have a nice day!")
@TwoSquirrels
Copy link
Author

前提として、luaqrcode/qrencode.lua をダウンロードして入れておく必要があります

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment