Jump to content
  • Sky
  • Blueberry
  • Slate
  • Blackcurrant
  • Watermelon
  • Strawberry
  • Orange
  • Banana
  • Apple
  • Emerald
  • Chocolate
  • Charcoal

Leaderboard


Popular Content

Showing content with the highest reputation since 05/03/20 in all areas

  1. 3 points
    Due to quarantine, I got a bunch of time in my hands, I remember that since I first used this mod, back in MC 1.7 we had this phrase on Geolyzer description "It is theoretically possible to eliminate this noise by scanning repeatedly and finding an average. (Unconfirmed, needs further testing.)" Now, many years later, i do know quite some about statistics and got plenty of time So, lets begin We begin our dive in Statistics with an hypothesis, Does this number measured relate to an ore or stone, putting it in hardness terms, does the true number of hardness equal to 3(ores) or 1.5(stone), now we estabilish our null hypothesis. The null hypothesis is which one of them I want to minimize the most(it will make more sense in a bit), I prefer to avoid losing ores, therefore my null hypothesis is that the true hardness of the number measured=3 unless proven with enough evidence the contrary(again, more sense later) In statistic we have two kind of erros given in the table below, we want to minimize error type 1 even if type 2 grows, not so much as for example, mark every where as ore, 0 type 1 error and 100% of type 2. Or be it random where both errors are 50%, so we have to have a small type 1 error without gigantic type 2 error ,let's try. I will from now on ommit "error" in type1 error. Let's first analyze on what I believe is the most traditional way, checking if a number is bigger than a threshold , I made a code to help me with that(DataCollector), given a line of 32 blocks, it will go through each one of them, measure 1000 times, and separate in intervals of 0,25. The result is given as every number smaller than the first column It's quite hard to see it all, comparing it with a stone would be disastrous, but we can clearly see a pattern of every 4th increasing in one more row, so make it goes from 0 to 4, to 8 to... to 56, when possible I tried to use an exact distance, for example 40=sqrt(12^2+32^2) when not possible a very close number 56=sqrt(32^2+32^2+32^2), compare stones and ores side by side and we get this: based on this we can see the strengths and weakness of a single or multiple threshold, in my code I tried to give a general approach, so if you want to detect any other pair it's possible. In my code, I used 2 variables, one to know the value when they meet, the distance where they meet, given by (Hhard-Lhard)*8, the second one is Havg, (Hhard+Lhard)/2, it gives the number when they meet. If the distance is smaller than "meet", the value will be bigger or lower than Havg but I will be sure after this check If we use a single threshold of >2.25 we would be fine up to 12, after our errors would be: 16: type1=10.9% ; type2=11.4% | 20: type1=21.7% type2=19.2% | 24:type1=24.7% type2=23.9% | 28: type1=29.8% type2=26.7% | 32:type1=29.6% type2=32.7% As we can see, not good, not terrible if we want to be sure that its a stone or an ore, we must know what is the distance, find this new threshold, it could be done for each one of them but I dont see a particular distribution, but every 4 distance we have a good approximation of a uniform distribution( where every value is as likely to happen), but between them it wont affect that much the chance of being wrong, type1 and type2 errors are equal to: 16: 24% | 20: 42% | 24: 50.4% | 28: 58.1% | 32: 61.8% | 36: 65.8% | 40: 70.5% | 44:73.9% | 48: 75.4% | 56: 78.5% it seems bad to do it that way, we said earlier that our objective is to minimize type1 error, but we can use multiple measurements to decrease the error, we were already going to do that anyway for our second kind of analysis, I chose to do 6 but you can pick another number, I tried to make it easy to change in the code, butt keep in mind that every measurement takes a lot of time and energy, using the geolyzer is time consuming, for each time you add 55 seconds to scan, default(6) is about 5min 30s. The new errors after six times are, (error)^6: 16: 0.1% | 20: 0.5% | 24: 1.6% | 28: 3.8% | 32: 5.6% | 36: 8,1% | 40: 12.2% | 44: 16% | 48: 18% | 56: 23.4% This is already much better, lower error compared to previus test, but this is still very high, we always have 32 distance, the height, with each command and with this test we would have a high chance missing anything, the robot would have to go around or increase the number of tests that adds up for a lot of time and energy, for such a poor result Now we begin using statistical theory, for that we need to know how the variance behaves given a distance, using Variance.lua code changing x and y and Variance1.lua to Z axis. This code gives Standard Deviation this represents how much from the mean it can be. plotting it in a graph we get that the standard deviation is given by approximately 0.35*distance. https://www.khanacademy.org/math/statistics-probability/sampling-distributions-library#what-is-a-sampling-distribution as this free class explains, the distribution of samples is a normal distribution, doesn't matter the original distribution. to visualize it, using DataCollector.lua code add ore nSamples to your code, default is 1, therefore it shows the original distribution now I am doing it with 6 samples the threshold for the average to given the Z value on the Ztable below is given by -Z*StandardDeviation(0.035*distance)/sqrt(nSamples(6)) + oreHardness(3)=OreT, Z is my type 1 error, my type2 error is given by StoneHardness*sqrt(nSamples)/(Z{positive now}*SD)=OreT. This is all a mess I know, lets give it some numbers, let's get a Z value equivalent of 5% wich is about 1.64 or 1.65, I will go with 1.64, distance of 56, we get -1.64*1.96/2.44+3=1,68, to find our type2 error we have Z=1.5*2.44/(1.96*1.68)=1.116 which is equal to 13.1% 16: type1=5% type2=0.6% | 20: type2=1.9% | 24: 3.6% | 28: 5.3% | 32: 7.2% | 36: 8.8% | 40: 10% | 44: 11% | 48: 12% | 52: 13% | 56: 13,5% We can see that this is the best analysis until now overall, mainly on bigger distances, also even though its hard to understand, its super easy to implement it on code, but this analisys and the last one can be thought as independent events if we do both analysis we significantly lower the chance of any error for 56 distance we have type1=1.1% type2= 3.5%, wich is super low, but for my code I chose to do it until 40 of distance or 32^2+32^2+16^2=40^2, for me 1.1% is huge yet and 16*32*32 is enough volume but you can tune it to whatever you like I want to bring to attention that the code of both analysis was done with the ability of taking any pair in mind, be it dirt and stone or stone and ore, or ore and diamond block, it must have a decent enough interval tho. also it checks for anything lower than stone or bigger than ore, must do another analysis to remove the ones you want from it. https://github.com/gabiiel/GeolyzerOreFinder PS: I am not a professional coder or anything like that, I just wanted to burn some quarantine time, any questions or improvements or features, I am willing to help. I hope everyone could understand. Also I am leaving up to you guys how to get to the ores, this is a very low end program that can run on any machine, the ores are stored in a string there is a function to help write and read from it DataCollector.lua variance.lua variance1.lua
  2. 1 point
    A while ago i made a JVM in Lua with the first goal of running Java on OC, just because i like Java and we all know it's a good programming language. So here's the JVM running a program that fills GPU: Oops, made a typo. Ok at first this might not seem impressive at all, but now you can interact with components using Java! That's an awesome thing for Java-ers like me! Race to first Java OS in OC? Anyone? Here's the (simple) code i used above import lukyt.oc.Component; public class ComponentTest { public static void main(String[] args) { String gpu = Component.getPrimary("gpu"); Component.invoke(gpu, "setBackground", new Object[] {0x2D2D2D}); Component.invoke(gpu, "fill", new Object[] {1, 1, 160, 50, " "}); System.out.println("Filled screen with RGB 0x2D2D2D"); } } Here's GitHub page (to download and use on your favourite OC computer): https://github.com/zenith391/lukyt
  3. 1 point
    How to Write an OpenComputers Operating System This is the kind of guide I wish I had had 9 months ago when I started developing operating systems for OpenComputers. It will walk you through writing a very basic OpenComputers operating system. EDIT: The guide I followed (the only one I found) is here: WARNINGS: -- This post assumes basic knowledge of programming, such as: what a string is, what a table or list is, what a function is, what a variable is, etc. I am NOT attempting to teach you Lua-- see warning #3 -- This operating system is not intended to have many features (nor is it fast!), but more so to teach new programmers. I will try to keep my code well-organized and well-commented, but no guarantees! -- If you're new to Lua, go read the PIL (https://lua.org/pil/1.html), and keep the Lua reference manual (https://lua.org/manual/5.3/manual.html) open in case you need to reference it. (I also recommend having the OpenComputers wiki [https://ocdoc.cil.li] open.) -- Note that the component, unicode, and computer APIs, plus checkArg, are unique to OpenComputers, and that io and package (plus require, loadfile, and dofile) must be defined by the user. -- This OS is structured in a way that is meant to be easy-to-follow, though you can lay your own out however you like. All source code is available at https://github.com/ocawesome101/basic-oc-os under no license in particular. 1. Init.lua init.lua is the file that most BIOSes expect to load. Most operating systems, this one included, simply use init.lua to load their kernel. This is an example of an init.lua file: -- The path to our kernel. Note that it is advisable to use the 'local' keyword in front of your variables unless you want them to be accessible from everywhere. local KERNEL_PATH = "/mini/kern.lua" -- Get the computer's boot address local address = computer.getBootAddress() -- Open the kernel file for reading local handle, err = component.invoke(address, "open", KERNEL_PATH) if not handle then -- The kernel is probably not present! error(err) end -- Read all the data from the kernel file local kernelData = "" repeat local chunk = component.invoke(address, "read", handle, math.huge) kernelData = kernelData .. (chunk or "") -- (chunk or "") protects against errors until not chunk -- End Of File -- Close the kernel file handle component.invoke(address, "close", handle) -- Try to turn the data we read into a function we can call local ok, err = load( kernelData, -- the data (or "chunk") "=" .. KERNEL_PATH, -- what name to use for the loaded chunk, prefixed with an "=" "bt", -- the mode with which to load the chunk. "bt" should be generally fine _G ) if not ok then -- There was probably a syntax error or some such thing error(err) end ok() -- Execute the kernel -- an idle loop in case the kernel exits. Could be replaced with computer.shutdown() while true do computer.pullSignal() end The comments should explain fairly well what is happening. init.lua should be placed in the root of your drive. 2. The Kernel The kernel is the heart of your operating system. In more advanced operating systems, the kernel usually contains some basic hardware management, a scheduler (relatively complex beasts involving coroutines and signal timeouts), and possibly drivers, leaving the rest of system initialization to the init process. For the sake of this tutorial, we will jump straight from a basic kernel to the shell. In my case, the kernel is at /mini/kern.lua, though yours can be almost anywhere. For compatibility's sake, it is usually a good idea to start by defining _OSVERSION like so: _G._OSVERSION = "mini 0.1.0" Next, you should find installed GPU and screen components- at least one of each. local gpu = component.list("gpu")() local screen = component.list("screen")() There are two ways to interact with components: component.invoke and through a proxy. For extended usage, a proxy is generally the better choice, but for one or two operations component.invoke is fine. An example of component.invoke usage: component.invoke( gpu, -- The component address, a string "bind", -- The method you want to invoke. Must be valid, and must be a string. screen -- Any additional arguments are interpreted as parameters ) It is probably a good idea to create proxies for the GPU and boot filesystem, since we're going to be using these things a lot. local gpuProxy = component.proxy(gpu) _G.fs = component.proxy(computer.getBootAddress()) -- computer.getBootAddress() is defined by most BIOSes Optionally, you can set up basic onscreen boot logging. This is especially useful for debugging, and it can give some insight into what your OS is doing when it boots. I set up my logging like this. Note that my code is somewhat over-commented for the sake of this tutorial. local line = 1 -- What line are we on? local width, height = gpuProxy.maxResolution() -- get the maximum resolution of the GPU and screen. These values are the minimum of the two. gpuProxy.setResolution(width, height) -- ensure that the screen resolution is properly set gpuProxy.fill( -- Fill a box on-screen with a single character 1, -- The top-left X coordinate 1, -- The top-left Y coordinate width, -- How wide the box should be height, -- How tall the box should be " " -- The character the box should be made of ) local function log(message) -- checkArg is a very useful function, used for argument checking. checkArg( 1, -- the argument number message, -- the argument itself "string" -- one or more types that are allowed for the argument, in the form of separate strings. ) -- Set the line at gpuProxy.set( -- Set a line (or part of a line) onscreen to a string 1, -- The X coordinate of the string line, -- The Y coordinate of the string message -- The string ) if line == height then -- We can't go down or we'll be off the screen, so scroll down a line gpuProxy.copy( -- Copy one screen area to another 1, -- The top-left X coordinate 1, -- The top-left Y coordinate width, -- The width height, -- The height 0, -- The relative X to copy to -1 -- The relative Y to copy to ) gpuProxy.fill(1, height, width, 1, " ") else line = line + 1 -- Move one line down end end -- Crash the system in a slightly prettier fashion. Not necessary, but nice to have. local function crash(reason) checkArg(1, reason, "string", "nil") -- This is an example of checkArg's ability to check multiple types -- Here, reason is already local; there is no need to specify it so reason = reason or "No reason given" log("==== crash " .. os.date() .. " ====") -- Log the crash header, ex. "==== crash 24/04/20 18:52:34 ====" log("crash reason: " .. reason) -- Log the crash reason. ".." is Lua's string concatenation operator. local traceback = debug.traceback() -- Tracebacks are usually useful for debugging traceback = traceback:gsub("\t", " ") -- Replace the tab character (not printable) with spaces (printable) for line in traceback:gmatch("[^\n]+") --[[ :gmatch("[^\n]+") splits the string on the \n (newline) character using Lua's basic regular expressions ]] do log(line) end log("==== end crash message ====") while true do -- Freeze the system computer.pullSignal() end end For sanity's sake (and ease-of-use) you should define loadfile(), dofile(), and require(). If you set up module caching in require, you can do some neat things that otherwise would be difficult, such as library persistence across programs. loadfile() is probably the most complex of the three: function _G.loadfile(file, mode, env) checkArg(1, file, "string") checkArg(2, mode, "string", "nil") checkArg(3, env, "table", "nil") -- Make sure mode and env are set mode = mode or "bt" env = env or _G -- env can be used for sandboxing. Quite useful. local handle, err = fs.open(file, "r") if not handle then return nil, err end local data = "" repeat local fileChunk = fs.read(handle, math.huge) data = data .. (fileChunk or "") until not fileChunk fs.close(handle) -- Always close your file handles, kids return load(data, "=" .. file, mode, env) end Then dofile: function _G.dofile(file) checkArg(1, file, "string") local ok, err = loadfile(file) if not ok then return nil, err end return ok() end Basic library caching can be setup very quickly with: local loaded = { ["gpu"] = gpuProxy } -- Libraries that have already been loaded Next, you should implement some sort of require() function. Ideally we'd do this with a fully fledged package library, but that's much more complex. First, you'll need paths of some kind to search. I did mine this way: local libPaths = { -- The path(s) to search for libraries "/mini/lib/?.lua", "/ext/lib/?.lua" } The standard Lua package.path is "/usr/share/lua/5.3/?.lua;/usr/share/lua/5.3/?/init.lua;/usr/lib/lua/5.3/?.lua;/usr/lib/lua/5.3/?/init.lua;./?.lua;./?/init.lua" but the above is considerably easier to parse. To disable package caching, useful for development, you can comment out the lines suffixed with "--". function _G.require(lib) checkArg(1, lib, "string") if loaded[lib] then -- return loaded[lib] -- else -- -- It wasn't already loaded, so search all the paths for i=1, #libPaths, 1 do component.proxy(component.list("sandbox")()).log(libPaths[i]:gsub("%?", lib)) if fs.exists( libPaths[i] -- The current path :gsub( -- Replace a character or characters in a string with another string "%?", -- The string to replace. "%" is necessary because string.gsub, string.gmatch, and string.match interpret "?", along with a few other patterns, as a form of regex (DuckDuckGo regular expressions if you don't know what regex is). lib -- The string with which to replace "?" ) ) then local ok, err = dofile(string.gsub(libPaths[i], "%?", lib)) -- string.gsub("stringToGSUB", ...) is the same as ("stringToGSUB"):gsub(...) if not ok then error(err) end loaded[lib] = ok -- return ok end end end -- end Once this is all done, I load my shell with a simple while true do local ok, err = dofile("/mini/shell.lua") -- Run the shell if not ok then crash(err) end end Note that mine is encased in a while loop so that if the shell exits it will restart. We aren't done yet though, as we still need to program the shell. 3. The shell For simplicity's sake, my shell is just a basic Lua interpreter. It doesn't seem to function properly in ocvm, unfortunately (pcall results are strange, I'll have to test in-game) but here it is, in all its glory: _G.term = require("term") _G.gpu = require("gpu") -- This is where the loaded = { ["gpu"] = gpu } line comes in term.clear() function _G.print(...) local args = {...} for k, v in pairs(args) do term.write(tostring(v) .. " ") end term.write("\n") end local currentDirectory = "/" -- self explanatory local function drawPrompt() gpu.setForeground(0x00FF00) -- Colors are stored as 24-bit hexadecimal values. (Look up "hexadecimal color"). 0x00FF00 is bright green. term.write("\n" .. currentDirectory .. " > ") gpu.setForeground(0xFFFFFF) end local function printError(err) gpu.setForeground(0xFF0000) term.write(err .. "\n") gpu.setForeground(0xFFFFFF) end local function execute(command) local ok, err = load(command, "=lua") if not ok then ok, err = load("=" .. command, "=lua") if not ok then return nil, err end end local result = {pcall(ok)} -- pcall, or protected call, captures errors. Very useful function. if not result[1] and result[2] then return printError(result[2]) end for i=#result, 1, -1 do print(result[i]) end end while true do drawPrompt() local command = term.read() if command ~= "\n" then execute(command) end end What is this shell doing? First, it defines a few utility functions (print(), drawPrompt, printError, and execute), and then it runs the shell's main loop. But wait! What about the top line? Where is the term API? It's in /mini/lib/term. I implemented term.getCursorPosition, term.setCursorPosition, term.scroll, term.clear, term.write, and term.read, but you can implement more fairly easily. Internally, there are two functions (showCursor and hideCursor) that I call after and before every operation, respectively. For example, when you call term.setCursorPosition(2, 2), the term API executes this code: function term.setCursorPosition(newX, newY) checkArg(1, newX, "number") checkArg(2, newY, "number") hideCursor() cursorX, cursorY = newX, newY showCursor() end cursorX and cursorY, as well as width and height, are used internally for purposes you can probably guess based on their names. If you need it, here's my term.read function (text input is always particularly tricky to get right): -- Fairly basic text imput function function term.read() local read = "" local enter = 13 local backspace = 8 local startX, startY = term.getCursorPosition() local function redraw() term.setCursorPosition(startX, startY) term.write(read) --.. " ") -- the extra space ensures that chars are properly deleted end while true do redraw() local signal, _, charID, keycode = computer.pullSignal() -- signal is the signal ID, charID is the ASCII code of the pressed character, and keycode is the physical keyboard code. Note that these are keypress-specific!! if signal == "key_down" then -- A key has been pressed if charID > 31 and charID < 127 then -- If the character is printable, i.e. 0-9, a-z, A-Z, `~!@#$%^&*()_+-=[]{}\|;':",./<>? read = read .. string.char(charID) elseif charID == backspace then -- The character is backspace read = read:sub(1, -2) -- Remove a character from the end of our read string elseif charID == enter then -- my god Kate's syntax detection is crap read = read .. "\n" redraw() return read end end end end That should be all! Feel free to comment if you need help (be sure to provide error messages and the relevant bits of your code) and I'll do my best to respond. My results with this code (I can enter Lua statements and they will be run): Next Steps: -- Expand on this OS with a more advanced shell, io and package libraries, and the like -- Write your own OS with a task scheduler using coroutines -- Be sure you give events to all processes! My first scheduler very much did not do that.
  • Newsletter

    Want to keep up to date with all our latest news and information?
    Sign Up
×
×
  • Create New...

Important Information

By using this site, you agree to our Terms of Use and Privacy Policy.