Advertisement
fatboychummy

ComputerCraft - Read, but only a number

Sep 18th, 2023 (edited)
1,442
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 6.75 KB | None | 0 0
  1. --- read_number, by fatboychummy
  2. --- You have full permission to use this in any way you see fit, as long as you don't claim it as your own.
  3. --- Do note this comes without liability, and I am not responsible for any damages caused by this code.
  4.  
  5.  
  6. --- This is a function that allows the user to input a number, and enforces that the input is a number.
  7. --- It also allows you to specify a minimum and maximum value for the number.
  8. --- Usage is at the bottom of the file.
  9.  
  10. --- Please note that this function has some slight amount of jank to it, and it's not perfect.
  11. --- In order to enter a negative value, for example, it is recommended you type
  12. --- in the positive version of the number you wish to enter *then* hit the negative sign.
  13. --- This is because the negative sign is used to negate the current number, and
  14. --- if you try to enter a negative sign before any numbers, it will drop in -0.
  15. --- If you try to get rid of the 0, it will also remove the negative sign.
  16.  
  17. -- ########################################################################
  18. -- # IF YOU WANT TO USE THIS FUNCTION IN YOUR OWN PROGRAM, COPY FROM HERE #
  19. -- ########################################################################
  20.  
  21. --- Like read(), but this will only allow the user to input an integer.
  22. ---@param min integer? The minimum value allowed.
  23. ---@param max integer? The maximum value allowed.
  24. ---@return integer value The number the user entered.
  25. local function read_number(min, max)
  26.   local input = {}
  27.   local len = 0
  28.   local position = 0
  29.  
  30.   min = min or -math.huge
  31.   max = max or math.huge
  32.   local function clamp(val)
  33.     if val > max then return max end
  34.     if val < min then return min end
  35.     return val
  36.   end
  37.   local function get_number()
  38.     return tonumber(table.concat(input)) or 0
  39.   end
  40.  
  41.   local x, y = term.getCursorPos()
  42.   local last_len = #input
  43.  
  44.   --- Redraw the input line.
  45.   local function redraw()
  46.     term.setCursorPos(x, y) -- move the cursor to the start of the line.
  47.     term.write((' '):rep(last_len)) -- clear the line
  48.     term.setCursorPos(x, y) -- move the cursor back to the start of the line.
  49.     term.write(table.concat(input) or "") -- write the input string.
  50.     term.setCursorPos(x + position, y) -- move the cursor to the correct position.
  51.   end
  52.  
  53.   -- Enable blinking to show the cursor to the user.
  54.   term.setCursorBlink(true)
  55.  
  56.   while true do
  57.     redraw()
  58.     last_len = #input
  59.     local event, key_char = os.pullEvent()
  60.     if event == "key" then
  61.       -- Handle backspace, enter, arrow keys.
  62.       if key_char == keys.backspace then
  63.         -- Remove the current character - 1 from the input string
  64.         if position > 0 then
  65.           table.remove(input, position) -- we use table.remove here so the table is compacted.
  66.           position = position - 1
  67.           if len > 0 then len = len - 1 end
  68.           if position < 0 then position = 0 end
  69.  
  70.           -- if backspace removed the negative sign, then we need to negate the number.
  71.           if input[1] == '-' then
  72.             local clipped = clamp(get_number())
  73.             input = {}
  74.             for char in tostring(clipped):gmatch(".") do
  75.               table.insert(input, char)
  76.             end
  77.             len = #input
  78.             position = 0
  79.           end
  80.         end
  81.       elseif key_char == keys.delete then
  82.         -- Remove the current character + 1 from the input string
  83.         if position < len then
  84.           table.remove(input, position + 1)
  85.           if len > 0 then len = len - 1 end
  86.           if position > len then position = len end
  87.         end
  88.       elseif key_char == keys.left then
  89.         -- Move the cursor left
  90.         position = position - 1
  91.         if position < 0 then position = 0 end
  92.       elseif key_char == keys.right then
  93.         -- Move the cursor right
  94.         position = position + 1
  95.         if position > len then position = len end
  96.       elseif key_char == keys.enter then
  97.         -- due to the fact that we enforce numbers only, we can assume that the input is always valid.
  98.         -- however, we should enforce it being in range.
  99.         local n = get_number()
  100.         if n < min or n > max then
  101.           -- if the number is out of range, then we should redraw the input line with the number in red.
  102.           local old = term.getTextColor()
  103.           term.setTextColor(colors.red)
  104.           redraw()
  105.           term.setTextColor(old)
  106.           sleep(0.25)
  107.         else
  108.           -- if the number is in range, then we can return it.
  109.           term.setCursorBlink(false)
  110.           print()
  111.           return n
  112.         end
  113.       end
  114.     elseif event == "char" then
  115.       -- Handle actual numbers, 0-9 (and the minus sign if min < 0)
  116.       if key_char:match("%d") then
  117.         -- Insert the number at the current position
  118.         local _position = position
  119.         if input[position + 1] == '-' then
  120.           -- handle attempting to insert in front of the negative sign.
  121.           _position = position + 1
  122.         end
  123.         table.insert(input, _position + 1, key_char)
  124.         position = _position + 1
  125.         len = len + 1
  126.       elseif key_char == '-' then
  127.         if min < 0 then
  128.           -- Negate the current number, clipping the value to the "maximum" for whatever side of the number line we're on.
  129.  
  130.           -- 1. Negate
  131.           if input[1] == '-' then
  132.             table.remove(input, 1)
  133.           else
  134.             table.insert(input, 1, '-')
  135.           end
  136.  
  137.           -- 2. Clip
  138.           local clipped = clamp(get_number())
  139.           input = {}
  140.           for char in tostring(clipped):gmatch(".") do
  141.             table.insert(input, char)
  142.           end
  143.           len = #input
  144.           position = len
  145.         end
  146.       end
  147.     elseif event == "paste" then
  148.       -- Handle pasted text, we need to enforce that this is a valid number.
  149.  
  150.       -- Use a very basic pattern to check if the pasted text is a valid number.
  151.       if key_char:match("^%-?%d+$") then
  152.         -- Clear the input and insert the pasted text.
  153.         input = {}
  154.         for char in key_char:gmatch(".") do
  155.           table.insert(input, char)
  156.         end
  157.         len = #input
  158.         position = len
  159.       end
  160.     end
  161.   end
  162. end
  163.  
  164. -- ######################################################################
  165. -- # IF YOU WANT TO USE THIS FUNCTION IN YOUR OWN PROGRAM, COPY TO HERE #
  166. -- ######################################################################
  167.  
  168. -- tests
  169.  
  170. write("Read number, no min/max: ")
  171. print(read_number())
  172.  
  173. write("Read number, min 0: ")
  174. print(read_number(0))
  175.  
  176. write("Read number, min 0, max 10: ")
  177. print(read_number(0, 10))
  178.  
  179. write("Read number, min -10, max 10: ")
  180. print(read_number(-10, 10))
  181.  
  182. write("Read number, min -10, max -5: ")
  183. print(read_number(-10, -5))
  184.  
  185. write("Read number, no min, max -5: ")
  186. print(read_number(nil, -5))
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement