/ tools / config.lua
config.lua
  1  --[[
  2      ToasterGen Spin
  3  
  4      Copyright (C) 2025 Clifton Toaster Reid <cliftontreid@duck.com>
  5  
  6      This library is free software: you can redistribute it and/or modify
  7      it under the terms of the GNU Lesser General Public License as published by
  8      the Free Software Foundation, either version 3 of the License, or
  9      (at your option) any later version.
 10  ]]
 11  
 12  local completion = require("cc.completion")
 13  local expect = require "cc.expect".expect
 14  
 15  local config = {}
 16  
 17  local function choice_impl(text, choices, add_space)
 18  	expect(1, text, "string")
 19  	expect(2, choices, "table")
 20  	expect(3, add_space, "boolean", "nil")
 21  	local results = {}
 22  	for n = 1, #choices do
 23  		local option = choices[n]
 24  		if #option + (add_space and 1 or 0) > #text and option:sub(1, #text) == text then
 25  			local result = option:sub(#text + 1)
 26  			if add_space then
 27  				table.insert(results, result .. " ")
 28  			else
 29  				table.insert(results, result)
 30  			end
 31  		end
 32  	end
 33  	return results
 34  end
 35  
 36  local function peripheral_extended(text, add_space)
 37  	expect(1, text, "string")
 38  	expect(2, add_space, "boolean", "nil")
 39  
 40  	local perifs = peripheral.getNames();
 41  	-- Add directions to the list of peripherals
 42  	local sides = { "top", "bottom", "left", "right", "front", "back" }
 43  	for _, side in ipairs(sides) do
 44  		table.insert(perifs, side)
 45  	end
 46  	return choice_impl(text, perifs, add_space)
 47  end
 48  
 49  --- Asks the user for a yes or no response
 50  ---@param message string The message to display
 51  ---@return boolean
 52  function config.askYesNo(message)
 53  	expect(1, message, "string")
 54  	local options = { "yes", "no" }
 55  	while true do
 56  		write(message .. " [Y/N]: ")
 57  		local response = string.lower(read(nil, nil, function(text)
 58  			return completion.choice(text, options)
 59  		end))
 60  		if response == "yes" or response == "y" then
 61  			return true
 62  		elseif response == "no" or response == "n" then
 63  			return false
 64  		end
 65  	end
 66  end
 67  
 68  --- Asks the user for a peripheral
 69  --- @param message string The message to display
 70  --- @return string
 71  function config.askPeripheral(message, expectedType)
 72  	expect(1, message, "string")
 73  	expect(2, expectedType, "string", "nil")
 74  	while true do
 75  		print(message .. ": ")
 76  		local response = read(nil, nil, peripheral_extended)
 77  		if response and peripheral.getType(response) then
 78  			if expectedType and peripheral.getType(response) ~= expectedType then
 79  				print("The peripheral is not of the expected type: " .. expectedType)
 80  				-- retry without recursion
 81  			else
 82  				return response
 83  			end
 84  		end
 85  	end
 86  end
 87  
 88  --- Ask monitor
 89  --- @param message string The message to display
 90  --- @return string
 91  function config.askMonitor(message)
 92  	expect(1, message, "string")
 93  	while true do
 94  		print(message .. ": ")
 95  		local response = read(nil, nil, peripheral_extended)
 96  		if response and peripheral.getType(response) then
 97  			if peripheral.getType(response) == "monitor" then
 98  				local monitor = peripheral.wrap(response)
 99  				if monitor.isColour() then
100  					monitor.setBackgroundColor(colors.red)
101  					monitor.clear()
102  					config.askYesNo("Is the correct monitor red?")
103  					monitor.setBackgroundColor(colors.black)
104  					monitor.clear()
105  					return response
106  				else
107  					print("The monitor does not support color")
108  					-- retry without recursion
109  				end
110  			else
111  				print("The peripheral is not a monitor")
112  				-- retry without recursion
113  			end
114  		end
115  	end
116  end
117  
118  -- Helper function to ask for user input
119  function config.askInput(message, default)
120  	expect(1, message, "string")
121  	expect(2, default, "nil", "string", "number")
122  	term.clear()
123  	term.setCursorPos(1, 1)
124  	print(message)
125  	if default ~= nil then
126  		write("Default: " .. tostring(default) .. " > ")
127  	else
128  		write("> ")
129  	end
130  	local input = read()
131  	if input == "" and default ~= nil then
132  		return default
133  	end
134  	return input
135  end
136  
137  -- Helper function to ask for a numeric input
138  function config.askNumber(message, default)
139  	expect(1, message, "string")
140  	expect(2, default, "nil", "number")
141  	while true do
142  		local input = config.askInput(message, default)
143  		local num = tonumber(input)
144  		if num ~= nil then
145  			return num
146  		end
147  		print("Please enter a valid number.")
148  		sleep(1)
149  	end
150  end
151  
152  -- Helper function to ask for an option from a list
153  function config.askOption(message, options)
154  	expect(1, message, "string")
155  	expect(2, options, "table")
156  	while true do
157  		term.clear()
158  		term.setCursorPos(1, 1)
159  		print(message)
160  		for i, option in ipairs(options) do
161  			print(i .. ") " .. option)
162  		end
163  		write("> ")
164  		local input = read()
165  		local num = tonumber(input)
166  		if num and num >= 1 and num <= #options then
167  			return options[num]
168  		elseif input ~= "" then
169  			-- Check if input matches option directly
170  			for _, option in ipairs(options) do
171  				if option:lower() == input:lower() then
172  					return option
173  				end
174  			end
175  		end
176  		print("Please enter a valid option.")
177  		sleep(1)
178  	end
179  end
180  
181  -- Helper function to ask for a URL
182  ---@param message string The message to display
183  ---@param default string|nil The default URL
184  ---@return string | nil The validated URL
185  function config.askURL(message, default)
186  	expect(1, message, "string")
187  	expect(2, default, "nil", "string")
188  	while true do
189  		local input = config.askInput(message, default)
190  		if input == "" and default ~= nil then
191  			input = default -- Ensure default is used if input is empty
192  		end
193  
194  		if input and input ~= "" then
195  			local ok, reason = http.checkURL(input)
196  			if ok then
197  				return input
198  			else
199  				print("Invalid URL: " .. (reason or "Unknown error"))
200  				sleep(1)
201  			end
202  		else
203  			return nil
204  		end
205  	end
206  end
207  
208  -- Configure peripheral devices
209  function config.configPeripherals()
210  	local peripherals = peripheral.getNames()
211  	print("Available peripherals: " .. table.concat(peripherals, ", "))
212  
213  	local carpet = config.askMonitor("Enter the carpet monitor name:")
214  	local ring = config.askMonitor("Enter the ring monitor name:")
215  	local redstone = config.askPeripheral("Enter the redstone input side:", nil)
216  
217  	local ivManagers = {}
218  	local ivCount = config.askNumber("How many inventory managers?", 1)
219  	for i = 1, ivCount do
220  		ivManagers[i] = config.askPeripheral("Enter inventory manager " .. i .. " name:", "inventoryManager")
221  	end
222  
223  	local ivmanBigger = config.askNumber("Base redstone signal strength:", 1)
224  	local modem = config.askPeripheral("Enter modem name:", "modem")
225  
226  	return {
227  		carpet = carpet,
228  		ring = ring,
229  		redstone = redstone,
230  		ivmanagers = ivManagers,
231  		ivmanBigger = ivmanBigger,
232  		modem = modem,
233  	}
234  end
235  
236  -- Configure reward multipliers
237  function config.configRewards()
238  	local numeric = config.askNumber("Reward multiplier for direct number bets:", 36)
239  	local dozen = config.askNumber("Reward multiplier for dozen bets:", 3)
240  	local binary = config.askNumber("Reward multiplier for binary bets:", 2)
241  	local colour = config.askNumber("Reward multiplier for color bets:", 2)
242  
243  	return {
244  		numeric = numeric,
245  		dozen = dozen,
246  		binary = binary,
247  		colour = colour,
248  	}
249  end
250  
251  -- Configure debug options
252  function config.configDebug()
253  	local debug = config.askYesNo("Enable debug mode?")
254  	local LokiURL = nil
255  	local TempoURL = nil
256  
257  	if debug then
258  		LokiURL = config.askURL("Enter Loki URL:", "http://localhost:3100")
259  		TempoURL = config.askURL("Enter Tempo URL:", "http://localhost:3100")
260  	end
261  
262  	return {
263  		loki = LokiURL,
264  		tempo = TempoURL,
265  	}
266  end
267  
268  -- Function to configure the table
269  function config.configTable()
270  	local toml = require("src.toml")
271  
272  	term.clear()
273  	term.setCursorPos(1, 1)
274  	print("ToasterGen Spin Configuration Utility")
275  	print("====================================")
276  
277  	-- Configure rewards and peripherals
278  	local rewards = config.configRewards()
279  	local devices = config.configPeripherals()
280  	local debug = config.configDebug()
281  
282  	-- Create the configuration
283  	---@type ClientConfig
284  	local fullConfig = {
285  		version = "0.1.0",
286  		rewards = rewards,
287  		devices = devices,
288  		debug = debug,
289  	}
290  
291  	-- Save the configuration
292  	local file = fs.open("/config.toml", "w")
293  	file.write(toml.encode(fullConfig))
294  	file.close()
295  
296  	print("Configuration saved successfully!")
297  	print("Press any key to exit.")
298  	os.pullEvent("key")
299  end
300  
301  return config