My brain wouldn't shut up last night and kept me awake until 9 AM designing a programming language based on Josh's beyond-awesome node interface. It's sad that it's probably Turing complete. Honestly, his node interface is screaming for a language to built on top of it and I don't even know if Josh realises how much potential it has in that department.
Hardenberg told me that my recent idea about CPU Allocation wasn't very good, because it makes things too fiddly for the player. And he's right, unless Josh has any ideas about how to make his power allocation mechanism easy to manipulate quickly. If Josh can make power allocation easy to manage, I'm guessing that CPU allocation should be easy to manage as well since it can be designed in a similar way.
But in any case, Hardenberg brought up the idea of using presets. It's not a new idea but it got me thinking about programs. Programs are like an expansion on the idea of presets. All presets are programs but not all programs are presets. Programs are something the player will be able to write whenever he's not otherwise busy and will tell the ship how to behave in different circumstances. Before the obvious objection is raised about how this will lead the computer to play the game for you and suck the fun out of everything, I'm considering limiting programs to only being able to control the power and CPU allocations of the vessel (at this point). Everything else will still be handled by the player - navigating the vessel, targeting ships, evasive maneuvering, etc.
I will detail more how these programs will work in another suggestion thread that I'm not sure I will post today. But I think the design of a programming language warrants its own thread. The language I'm designing is a dynamically-typed graphical dataflow language vaguely based on LabVIEW and designed to work using Josh's node interface. At the moment I'm calling it ViTheory.
I believe a programming language would be beneficial to Limit Theory for these reasons:
- Many if not most of the people here have programming experience. A lot of you program for work if not as a hobby as well. You may find that writing programs in this language for your vessels is quite fun.
- It opens up the opportunity to have members of the community share, discuss and collaborate on designing these programs.
- "Hey, Cornflakes, what do you think of this program I wrote? It's designed to help improve the DPS of frigates against battlecruisers.
- "That's pretty cool, actually, I just finished writing one that will help frigates tank better against bigger ships. Maybe we could try integrating them and see how that plays out?"
- This idea is not unique and was the main feature of Notch's upcoming game 0x10c. Notch created an assembly language called DCPU-16 with which people could write programs for their ships. The 0x10c subreddit has over 6000 subscribers. That means at least 6000 people expressed some interest in a space-sim game whose primary feature is writing programs for your ship's main computer. After Notch cancelled development of the game, the community even proposed trying to finish the game themselves. These poor fellows are so desperate for a game like 0x10c that they're trying to delude themselves that No Man's Sky will be even remotely similar to it. If Josh adds a programming aspect to Limit Theory, he has the potential to attract a wider audience to the game.
- The language I propose is graphical and relies on Josh's node interface. Not only will this make it visually appealing, it should also be pretty intuitive and easy to learn, potentially even for non-programmers. Anyone familiar with the usual procedural constructs such as conditionals, loops, etc. should be able to pick it up fairly easily. ViTheory is based loosely on LabVIEW, which is one of the easiest and most intuitive languages I've ever come across. It took only around two days of using it for me to be about as good at it as my dad, and I was able to begin using it for serious, practical work on the third.
- Cornflakes_91 also suggested a pretty awesome idea that would rely on a dataflow programming language in Custom UI parts and macros.
Everything you program will involve adding, editing and connecting nodes together. Nodes can accept inputs from other nodes, carry out some kind of processing and potentially modify the state of its inputs, and may also produce outputs. All nodes have a "hidden" input that accepts what is called flow. Flow is used to control the order of execution of components within the program where sequential execution is necessary. The flow input can be left unconnected, and when this happens the node will attempt to execute as soon as the conditions for its execution are valid. This leads to massive parallelism which is a common feature among graphical dataflow languages. I propose seven types of nodes:
Figure 1: Node types in ViTheory (direct link)
- Constants can be any base type: strings, integers, floats, etc. Like their name implies, their value cannot change. Because this language is dynamically typed, their type is determined at run-time.
- Variables are like constants, but their value is able to change. Variables must be identified by a string of numbers and letters, and this string must begin with a letter of any case. Uppercase and lowercase letters are allowed.
- Program flow are nodes that control which paths of execution a program takes. These include conditionals and loops.
- Functions are sections of the program that perform specific tasks. They can be specified to accept any number of any types of input as parameters and return any number of any types of output. They can be called recursively. All the nodes that comprise the signature and definition of a function act as sub-nodes to the function node and occur at the next depth layer in the program node tree. This makes identifier scope extremely intuitive in ViTheory. All the nodes that are children of the same node as a variable node is are the siblings of that variable and together constitute its sibling space. The scope of a variable is just its sibling space. In other words, variables you declare inside functions will be limited to the scope of the function and not beyond it.
- Objects are entities comprised of constants, variables, functions and other objects.
- Arrays are collections of constants or objects that are all of the same data type.
- Generic nodes are used where no other nodes are appropriate. They provide the backbone of the language.
Any variable can have assigned to it a constant, another variable or the output from a function.
Objects can be assigned to other objects that expose the same number and type of constants, variables and functions (see below).
Figure 2: Assignment in ViTheory (direct link)
Arithmetical operations can be performed using arithmetic functions on constants, variables or the outputs from other functions.
Figure 3: Arithmetic in ViTheory (direct link)
Objects expose their constants, variables, functions and child objects as shown below. If objects have the same numbers and types of constants, variables, functions and child objects, they are considered to be of the same type.
Figure 4: Objects in ViTheory (direct link)
Arrays can be used to store an ordered collection of constants or objects of the same type. Array nodes expose a length variable that is equal to the number of items stored within the array. Array elements can be accessed through an integer input to the array node that specifies the index of the element. Arrays are one-indexed in ViTheory.
Arrays can be appended to and have elements from them removed, among other things.
Figure 5: Arrays in ViTheory (direct link)
You can cause a program's execution to branch along different paths by using conditionals. ViTheory contains one kind of conditional node: IF.
The IF node accepts a boolean value, typically from a conditional function (">", "<", "==", etc.). It provides two outputs - program flow moves along one of these outputs if the conditional value is true and moves along the other if the conditional value is false.
One IF node alone corresponds to the standard "if...else" constructs of procedural languages. IF nodes can be chained together to produce constructs analogous to if...else if...else if...else. This is done by connecting the false output of an IF node to the flow input of the succeeding IF node.
Figure 6: Conditionals in ViTheory (direct link)
Loops are useful when you wish to execute the same kind of behaviour from a program multiple times. FOR nodes are used when you want to execute behaviour a specific number of times, whereas WHILE nodes are used when you want to execute behaviour until some condition is satisfied (or not).
FOR nodes accept an integer input (from either a constant, variable or function output) that specifies the number of iterations to go through. They also accept two sources of flow: one from the node triggered just before the for loop is entered, and another from the last node in the body of the loop. When the current iteration of the for loop is less than or equal to the total number of iterations set for it, the node will output flow to the first node in the body of the loop. When the current iteration exceeds this value, the node will output flow to the next node its connected to outside of the loop body. FOR nodes also expose the current iteration count as an integer value.
WHILE nodes are similar to FOR nodes but accept a boolean input rather than an integer one. The WHILE node will continue to iterate while the boolean value is true.
Figure 7: Loops in ViTheory (direct link)
The player will be able to define his own functions by adding a Function node to the program and specifying its identifier (name), and then entering into the node (going one level deeper into the program tree) to define the signature and behaviour of the function.
The signature of a function is a specification of the parameters that the function accepts and the outputs it will return. When you traverse into a function's child-space, you will see the function node but with two generic nodes attached to it. One of these is called Parameters and the other is called Returns. You can specify the parameters of the function by connecting variable and object nodes to it - these variables and objects will be bound to the state of the arguments passed to the function when it is called, and will have a scope that encompasses the entire child-space of the function node. You can similarly specify variables and objects to return from the function by connecting the appropriate nodes to the Returns node.
Functions in ViTheory are pass by value. They cannot modify the state of inputs passed to them. If you want a function to change the state of its inputs, you can make the function output a copy of the input with the appropriate modifications made and then assign the copied input to the original input.
Functions are called by including the function node in a program and passing flow to it. The function will not execute if any of its inputs are invalid at the moment flow is passed to it, and will block execution until its inputs are all valid.
Functions terminate when no more flow is possible within its child-space i.e. there is no more flow occurring between the nodes that comprise its definition. At that moment, the state of any variables and objects used within the function definition that are also connected to the Returns node of the function signature will be returned as the output of the function. Flow will then pass back to the node following on from the function node.
If the player has multiple nodes in his program corresponding to the same function, editing any one of them will cause the changes to be reflected in all of the nodes within the same scope.
Figure 8: Function definitions in ViTheory (direct link)
Figure 9: Hello World function definition in ViTheory (direct link)
Figure 10: Loop-based implementation of the Fibonacci function in ViTheory (direct link)
Figure 11: Recursive implementation of the Fibonacci function in ViTheory (direct link)