Created
January 30, 2025 03:59
-
-
Save TwoSquirrels/103eef8469ead0d619ce002618be709b to your computer and use it in GitHub Desktop.
QR code generator for Computer Craft
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
-- 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!") |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
前提として、
luaqrcode/
にqrencode.lua
をダウンロードして入れておく必要があります