ksp-kos / kos Goto Github PK
View Code? Open in Web Editor NEWFully programmable autopilot mod for KSP. Originally By Nivekk
License: Other
Fully programmable autopilot mod for KSP. Originally By Nivekk
License: Other
Using version v12.0P3 the following code:
set tableCol to 2.
print ship:vesselname at (tableCol, 2).
results in a syntax error with the arrow pointing at the "t" in "tableCol".
Using
print ship:vesselname at (2,2).
works as expected.
And just cause I was confused before, this is the version on https://github.com/KSP-KOS/KOS/releases with the files in the zip file all dated April 7.
The parameter list of a script is passed to it backwards. Example script test.txt:
declare parameter p0.
declare parameter p1.
declare parameter p2.
declare parameter p3.
print "0: " + p0.
print "1: " + p1.
print "2: " + p2.
print "3: " + p3.
Call it from the terminal as follows:
run test(0, 1, 2, 3).
The expected result would be:
0: 0
1: 1
2: 2
3: 3
But, the actual result is:
0: 3
1: 2
2: 1
3: 0
I believe this bug also effects some or all built-in functions (but not constructors):
print mod(21, 6).
According to the documentation, this command should return 3. However, it actually returns 6. But, it gives the expected answer if we flip the parameters:
print mod(6, 21).
In previous versions of kOS you could write this in immediate mode at the terminal prompt and it would wait until you entered the final end brace ("}") before executing the "until" command:
set x to 1.
until x > 5 {
print x.
set x to x + 1.
}.
1
2
3
4
5
But in this new version it attempts to evaluate the "until" expression immediately as soon as you type the header line and hit 'enter', and thus gives a syntax error.
set x to 1.
until x > 5 {
syntax error at line 1
until x > 5 {
^
In the new version you'd have to type it all in in one line to make it work, like so:
until x > 5 { print x. set x to x + 1.}.
I noticed this because I was trying to test a few things out and therefore was typing syntax in at the terminal.
There's more than one IDE people made to handle kosscript, and some people have tried making syntax definition configurations for them, and other editors that support that type of thing (I maintained one for VIM but it's out of date now).
One shared problem I think all such people would have (I assume it's a shared problem) is that a lot of what you want to highlight has to be configured by reading the C# code here by eyeball, finding all the cases of things you want to highlight, and then manually coding those strings into the syntax def file. That makes keeping it updated a bit hard.
You can get the base syntax by reading the TinyPG configuration, but it only contains half of what you need to know. It tells you, for example, that a function call looks like:
something left-paren something right-paren
But it doesn't tell you that, for example, there is a built-in sqrt() function but not a built-in cuberoot() function. To get the list of all those terms you have to read through the code.
It doesn't tell you that BODY is a built-in special variable name but PLANET isn't.
It doesn't tell you that, for example, ":DELTAV" is a defined suffix for a NODE, but not for a BODY.
I was considering whether or not it would possible to put terms like that into some standardized form for people to pull out and use in syntax highlight configurations. It could just be as simple as having a bit of C# code that uses reflective methods to iterate over all the existing subclasses of the FunctionBase class, the BoundVariable class, and the SpecialVariable classes and prints out their configured strings. (Assuming the strings were written in a way that stuffs them into member variables instead of using string literals.)
Obviously bugs should be fixed as solutions present themselves, but what really draws people in are new things to do.
Would it be a good idea to have a sort of brainstorming session of wishlist feedback from people, to help drive what 0.13 should focus on?
At any given moment there's always going to be more new features that people want than is reasonable to actually plan for just one release, but getting a feel for which things are most important to the user community might be a good idea to help prioritize which things to work on first.
But then again, when development is entirely on voluntary time, asking developers to work on a thing that the developers don't think is important, or is uninteresting to them, is a sure way to stall the project.
So to find a good compromise between those two competing factors of "give the users what they want" and "let developers work on what they want", I was contemplating this: First, potential developers here discuss what things they'd like to work on - ideas they have for future enhancements. Then second, a list of those things is presented on the forum for discussion. The intersection of "set of things developers want" and "set of things users want" becomes the basis for picking the next goals.
I'm just throwing a random idea out through bleary eyes too early in the morning. I don't know if I explained it well, but we'll see. What do people think of this?
This used to work in the old pre-kRISC version of kosscript:
set flag to 1.
if flag { print "flag is true". }.
But now any integer value of any kind, when interpreted as a boolean value, returns false, as opposed to the normal boolean meaning of zero=false, nonzero=true.
Hello,
The game seems to crash when paused and the kos console is running:
ArithmeticException: NAN
at System.Math.Sign (Double value) [0x00000] in <filename unknown>:0
at kOS.Utilities.SteeringHelper.Sign (Vector3d vector) [0x00000] in <filename unknown>:0
at kOS.Utilities.SteeringHelper.GetEffectiveInertia (.Vessel vessel, Vector3d torque) [0x00000] in <filename unknown>:0
at kOS.Utilities.SteeringHelper.SteerShipToward (kOS.Suffixed.Direction targetDir, .FlightCtrlState c, .Vessel vessel) [0x00000] in <filename unknown>:0
at kOS.Binding.FlightControlManager+FlightCtrlParam.SteerByWire (.FlightCtrlState c) [0x00000] in <filename unknown>:0
at kOS.Binding.FlightControlManager+FlightCtrlParam.OnFlyByWire (.FlightCtrlState& c) [0x00000] in <filename unknown>:0
at kOS.Binding.FlightControlManager.OnFlyByWire (.FlightCtrlState c) [0x00000] in <filename unknown>:0
at (wrapper delegate-invoke) FlightInputCallback:invoke_void__this___FlightCtrlState (FlightCtrlState)
at (wrapper delegate-invoke) FlightInputCallback:invoke_void__this___FlightCtrlState (FlightCtrlState)
at Vessel.FeedInputFeed () [0x00000] in <filename unknown>:0
at FlightInputHandler.FixedUpdate () [0x00000] in <filename unknown>:0
(Filename: Line: -1)
Look rotation viewing vector is zero
(Filename: Line: 62)
Look rotation viewing vector is zero
(Filename: Line: 62)
Invalid parameter because it was infinity or nan.
(Filename: Line: 137)
Invalid parameter because it was infinity or nan.
(Filename: Line: 137)
Invalid parameter because it was infinity or nan.
(Filename: Line: 137)
Invalid parameter because it was infinity or nan.
(Filename: Line: 137)
Invalid parameter because it was infinity or nan.
(Filename: Line: 137)
Invalid parameter because it was infinity or nan.
(Filename: Line: 137)
Invalid parameter because it was infinity or nan.
(Filename: Line: 137)
Invalid parameter because it was infinity or nan.
(Filename: Line: 137)
Invalid parameter because it was infinity or nan.
(Filename: Line: 137)
Invalid parameter because it was infinity or nan.
(Filename: Line: 137)
Invalid parameter because it was infinity or nan.
(Filename: Line: 137)
Invalid parameter because it was infinity or nan.
(Filename: Line: 137)
Invalid parameter because it was infinity or nan.
(Filename: Line: 137)
Invalid parameter because it was infinity or nan.
(Filename: Line: 137)
Invalid parameter because it was infinity or nan.
(Filename: Line: 137)
Invalid parameter because it was infinity or nan.
(Filename: Line: 137)
Invalid parameter because it was infinity or nan.
(Filename: Line: 137)
Invalid parameter because it was infinity or nan.
(Filename: Line: 137)
Invalid parameter because it was infinity or nan.
(Filename: Line: 137)
Invalid parameter because it was infinity or nan.
(Filename: Line: 137)
Invalid parameter because it was infinity or nan.
(Filename: Line: 137)
dest.radius>=0.0f
(Filename: ..\..\Core\Common\src\BoxShape.cpp Line: 126)
dest.radius>=0.0f
(Filename: ..\..\Core\Common\src\SphereShape.cpp Line: 96)
dest.radius>=0.0f
(Filename: ..\..\Core\Common\src\CapsuleShape.cpp Line: 133)
dest.radius>=0.0f
(Filename: ..\..\Core\Common\src\CapsuleShape.cpp Line: 133)
dest.radius>=0.0f
(Filename: ..\..\Core\Common\src\BoxShape.cpp Line: 126)
dest.radius>=0.0f
(Filename: ..\..\Core\Common\src\SphereShape.cpp Line: 96)
dest.radius>=0.0f
(Filename: ..\..\Core\Common\src\CapsuleShape.cpp Line: 133)
dest.radius>=0.0f
(Filename: ..\..\Core\Common\src\CapsuleShape.cpp Line: 133)
Look rotation viewing vector is zero
(Filename: Line: 62)
Crash!!!
This is more of a question than an issue but I wan't to know what you think about this. Right now locks are NOT available in a child program but (as I read in the forums) people may think that that is the correct behavior.
set x to 1.
until x > 1000 { print x. set x to x + 1. }.
1
2
3
3
4
5
// (and so on......)
I can't hit Control-C to interrupt that printing. It goes all the way to the end of 1000. I think I used to be able to hit Control-C to stop execution.
The properties of stage:liquidfuel and stage:oxidizer are returning zero for the new KSP 0.23.5 parts that are a hybrid combination being both an engine and a fuel tank at the same time.
One example of such a part is the "LFB KR-1x2".
I can understand if the EDIT command is a pain to re-implement but it was once the only good way to read files on the local drive. If you did a LOG "MESSAGE" TO FILENAME command on the local drive, it gets stored in the persistence file and its hard to have to read it there instead of in the terminal window.
Also, if you want to check "is this the program that took 2 parameters or 3 again?" You can't currently look at it from the terminal to see.
Perhaps a primitive equivalent to the unix/dos 'more' program should be implemented so people can see what's in the files from the terminal window. It would be a lot simpler to implement than a full editor. It would just show a screenful of text and wait for "enter' to show the next screenful of text, and so on.
This works:
print someList#1.
This doesn't:
set idx to 1.
print someList#idx.
Is there a reason that you can only use literals with the "#" operator? Was that a deliberate decision? At the moment the only way to access the elements of a list is to iterate over them with the list's for loop. There seems to be an ':iterator' suffix but I don't think it's usable from within the script. You can't add or subtract or dereference the iterator so it's not useful in the way that iterators normally are.
I have the latest release, titled "Better new parser!", when I execute the following in the in-game console nothing happens.
switch to archive.
edit asdf.
When pressing enter on edit asdf.
the prompt simply moves to the next line like it does with many other commands. There is no error message.
If I edit files directly using Notepad the programs are visible and can be run in-game.
print 2 >= 1 and 2 > 1.
False.
print (2>=1) and (2 > 1).
True.
I suspect this is happening because the 'and' and 'or' operators aren't waiting for the <, <=, =, >=, and > operators to be evaluated first, leading the expression to end up trying to do things like this:
print ((2>=1) and 2) > 1.
which leads to asking if True is greater than 1.
If you set up a 'when' trigger designed to interrupt you in the future, and then lock a variable to an expression in the mainline code, then when the 'when' condition triggers, inside the 'when' clause the lock variable is treated as "undefined".
Here's a small example program demonstrating it:
set startTIme to time:seconds.
set done to 0.
// Set up a trigger to occur 5 seconds from now:
when time:seconds > startTime + 5 THEN {
print "WHEN/THEN."
print "in When/then body, elapsed time = " + secs.
set done to 1.
}.
// Use the number of seconds elapsed as an example lock variable:
lock secs to (time:seconds - startTime).
print "Expect an interruption in 5 seconds...".
until done = 1{
print "In mainline program, elapsed time = " + round(secs,0).
wait 1.
}.
The expected result should be:
Expect an interruption in 5 seconds...
in mainline program, elapsed time = 0
in mainline program, elapsed time = 1
in mainline program, elapsed time = 2
in mainline program, elapsed time = 3
in mainline program, elapsed time = 4
WHEN/THEN.
In when/then body, elapsed time = 5
The actual result is this:
Expect an interruption in 5 seconds...
in mainline program, elapsed time = 0
in mainline program, elapsed time = 1
in mainline program, elapsed time = 2
in mainline program, elapsed time = 3
in mainline program, elapsed time = 4
WHEN/THEN.
Variable secs is undefined
Sometimes sending a double with a value of NaN or Inifinity to KSP causes the game to crash showing a black screen.
To avoid this we should sanitize all the values sent to KSP and raise an exception if we found either of this ones.
This syntax worked in previous versions of kOS:
LOCK someVar TO HEADING expr1 By expr2.
LOCK STEERING to someVar.
The expression "heading expr1 by expr2" doesn't seem to be recognized as valid syntax in this new version. What it used to do was create a Euler rotation pointing at compass heading expr1 pitched up expr2 degrees above the horizon.
The previous version of kOS used:
list DOCKINGPORTS from VESSEL in x.
The new syntax:
LIST (IDENTIFIER (IN IDENTIFIER)?)? EOI;
Any ideas on what the best method for a dockingport command would be?
Is there a way to get the surface retrograde?
Very useful for lander scripts
print 1.23 * 10^35.
-2641404887.04
I looked at that and thought, "that can't be right". It's the wrong magnitude, wrong digits, and wrong sign.
After a bit of investigating, I think what it's doing is calculating 10^35 using entirely integer operations, thus getting an overflow result of the 32-bit minimum integer (-2147483648), and then multiplying that by 1.23.
As the example below shows, it doesn't matter if the value is inside variables in the expression or written as "raw" literals in the expression. Also, if the value is written with a ".0" suffix it forces the system to stop treating it as an integer and works as a temporary workaround:
set mantissa to 1.23.
set exponent to 35.
set exponentPointZero to 35.0.
print mantissa * 10 ^ exponent.
-2641404887.04
print mantissa * 10 ^ exponentPointZero
1.23E+35
The current way the parser is implemented, you can go up to 10^9 safely, but when you go to 10^10 or higher, this problem triggers unless you explicitly write one term with a decimal point, which isn't surprising because that's where the value exceeds signed integer32 max int.
I noticed that the divide operator (/) seems to be able to divide two integers without triggering integer-style truncation, i.e "print 1/2." returns 0.5, rather than 0, so maybe whatever was done for that can be done for the exponent operator (^) too.
This stat seems like it should be really useful for predicting SOI changes, but as far as I can tell the number it returns is always too high - sometimes by quite a lot.
To demonstrate: manually set up an encounter with the Mun, then run the following command in the KOS terminal:
add node(time:seconds + ETA:transition, 0, 0, 0).
Go to map view and focus on the Mun. Compare the ETA reported by the stock UI for the encounter to that of the new maneuver node, and you will see that they do not match as they should.
When I tried this a second ago, the node was placed about 5 minutes after the actual transition; that is the closest I have seen it. Earlier tests averaged a separation of more like 20 minutes, but sometimes it is off by hours.
Sometimes I will accidentally drag the kos window off of the screen. After this, I cannot access the kos window anymore.
I must reinstall the plugin to fix the problem; this is very annoying.
To reproduce, drag the kos window rapidly off the screen when not in fullscreen.
The UNLOCK command does not support the ALL modifier to release all active locks.
It would nice to have the possibility to define keys oder keycombinations that when hit trigger some action in my script
For example: a program that implement some sort of autopilot that should accept commands.
Issue:
When looking at the readings from VELOCITY:SURFACE:Z, they seem to be somehow mixed with VELOCITY:SURFACE:X. Upwards velocity seems to induce an 'Eastward' bias in the Z vector, with downwards velocities giving a 'Westward' bias.
Example Program:
declare lastClear.
declare vel.
clearscreen.
set lastClear to time.
lock vel to velocity:surface.
until 0 {
print " vSped:" + round( VERTICALSPEED, 2 ) at (0,8).
print " V:X:" + round( vel:X, 2) at (0,9).
print " V:Y:" + round( vel:Y, 2) at (0,10).
print " V:Z:" + round( vel:Z, 2) at (0,11).
//Clear Screen Errors
if time-lastClear > 3 { clearscreen. set lastClear to time. }.
}.
Simple test probe will demonstrate issue on vertical launch.
More Detailed Example:
declare tAlt.
declare p.
declare i.
declare d.
declare error.
declare integError.
set integError to 0.
declare corY.
declare corZ.
declare vSpd.
declare sSpd.
declare vertMix.
declare velMag.
set tAlt to 1000.
set p to 0.06.
set i to 0.001.
set d to 0.25.
lock error to (tAlt - ALTITUDE).
lock vSpd to VERTICALSPEED.
set sSpd to SURFACESPEED.
clearscreen.
declare lastClear.
set lastClear to time.
lock vel to velocity:surface.
lock velMag to velocity:surface:mag.
LOCK THROTTLE TO p*error + i*integError + d*vel:x.
set corY to vel:Y/1.
set corZ to vel:Z/-1.
lock steering to up + R(corY,corZ,0).
until 0 {
print " pError:" + round( p*error*100, 2 ) at (0,1).
print " iError:" + round( i*integerror*100, 2 ) at (0,2).
print " dError:" + round( d*vel:x*100, 2 ) at (0,3).
print " TrueErr:" + (tAlt - ALTITUDE) at (0,5).
print "Throttle:" + round( throttle*100, 2 ) at (0,7).
print " V:X:" + round( vel:X, 2) at (0,9).
print " V:Y:" + round( vel:Y, 2) at (0,10).
print " V:Z:" + round( vel:Z, 2) at (0,11).
print " cos(Y):" + cos( vel:Y ) at (0,13).
print "VelMag:" + velMag at (0,16).
print "VertMix:" + vertMix at (0,17).
set integError to (integError + error).
set sSpd to max( SURFACESPEED, 1).
// Bound Check
if integError*i > 1 { set integError to 1/i.}.
if integError*i < -1 { set integError to -1/i.}.
set vertMix to min( abs( vSpd ), 10 ) / sSpd .
// Bounds check and set steering vectors
set corY to vel:Y/1.
set corY to min( max( corY, -90 ), 90 ).
set corZ to vel:Z/-1.
set corZ to min( max( corZ, -90 ), 90 ).
//Clear Screen Errors
if time-lastClear > 3 { clearscreen. set lastClear to time. }.
}.
This controller seems to work fine in the 0.23 patch of the game, but shows the error documented above with the 0.23.5 'patch'.
It is interesting to note that the controller WILL settled to a zero horizontal speed eventually. This happens after the up-down oscillations stop (The PID controller is tuned for a critically dampened response for a very specific craft, most others will 'bob' for a while, then settle). During the final up-down motions, the craft will steer heavily east and west as it attempts to counter velocity that doesn't exist...
I was about to build a release to share with everyone, then i realized i don't really know about all of the changes additions you made other than the major parser rewrite.
ill try to dig through the git log and find what i can
The change for #14 broke the ability to store infinity in a variable.
Now if there is going to be a chance that there might be a divide by zero anywhere in an expression, that has to be checked for ahead of time, rather than allowing it to return infinity. As soon as any math expression or sub-expression returns infinity, the script dies.
That means things like this:
set var to x / y.
If there's a chance Y might be zero, have to be turned into this instead:
set var to bigNum.
if y > 0 or y < 0 {
set var to x / y.
}.
(I'd use if/else instead for that if there was an 'else'.)
This makes writing lock expressions currently a bit of a pain because there's no mechanism currently to protect a lock expression with an "if" like you can with a 'set'. The value of a lock must be written out as a single expression, which you must assume could be executed at any random time not directly under your control (because flybywire might execute it even if you don't).
So what I'm talking about is the ability to do this:
lock var to x /y, unless y happens to be zero at this instant, in which case lock it to bigNum instead.
With a 'set' command, you can control when the check happens as shown above.
But since a lock has to all be one expression, the technique used for the set isn't quite right.
If something like C's trinary operator "?" was implemented, then you could protect lock expressions like this:
lock var to ( (y = 0) ? bigNum : x /y)
Obviously the syntax there isn't quite right for kosscript's terms. Perhaps something like:
lock var to CHECK y = 0 TRUEVAL bigNum FALSEVAL x/y .
might look a bit more 'kosscript'-like.
As it stands, the only way to protect the expression is to lie about the contents of 'y' and pretend it's something like 0.000001 whenever it's really 0.0, which if you're using it elsewhere besides this lock expression, could be messy.
That's what I'm doing for now in my script to deal with this new change. When the air pressure is really 0, I'm pretending it's 0.000001 just to keep the script from barfing out.
The else statement extremely necessary.
Example syntax:
IF stmt {
run foo.
}
ELSE {
run bar.
}
IF stmt {
run foo.
}
ELSE IF stmt2 {
run baz.
}
This is just a reminder to @erendrake that when 0.12 is released, there's some documentation updates to the MD files over in the KOS_DOC project that go along with it (like the list [] syntax). But they haven't been run through the doc generation process, and aren't public yet.
(On a side note, there's one thing over there that I don't know how to document because I don't actually know the answers myself. Can you have a look at this issue, @marianoapp, and see if you can help? It's this one: KSP-KOS/KOS_DOC#5 )
Previously I had created issue #26 about integers not working as booleans. It was closed as un-reproducable.
But I just discovered how to re-produce it. The error only appears when using UNTIL statements, and does not appear in IF statements.
In other words:This works as expected:
set x to 0.
if x then { print "x is true". }.
But this does not.
set x to 0.
until x { print "X is still false.". }.
The error is in the implementation of UNTIL, not of IF.
The above until loop should do this:
X is still false.
X is still false.
X is still false.
X is still false.
[ continues like that forever until CTRL-C ]
But instead it just exits the loop immediately, as if 0 meant "true".
Prior to saving, list the local drive and you see something like this:
list
Volume #1
Name Size
------------------------------------
prog1 420
prog2 1200
Free space remaining: 8380
Then, exit the game.
Then re-run the game, go to the Tracking Center, and switch back to the vessel again and take a look at the local drive again. Now you see this:
list
Volume #1
Name Size
------------------------------------
prog1 0
prog2 0
Free space remaining: 10000
The filenames are there, but with nothing in them.
It does not happen with the archive - only with local volumes. So I assume it has something to do with loading/saving the contents in the persistence file.
Please let me know what you think of this idea, especially @marianoapp and @erendrake
When I implemented the ELSE operator (issue #6), it was my first attempt to alter the syntax definition, and I was nervous about breaking something so I checked it afterward with the following kosscript designed to try to cover every possible permutation of IFs and ELSEs that I could think of, including trying to regression-test for things that shouldn't have changed about it:
//KOS
// A script with the purpose of testing that 'if' and 'else' work right:
set a to 10.
set b to 20.
set c to 40.
print "IF, one line body, true check:".
print "is " + a + " = 10?".
if a = 10
print " yes.".
print "IF, one line body, false check:".
print "is " + b + " = 10?".
if b = 10
print " yes.".
print "IF, two line body requiring braces:".
print "is " + a + " = 10?".
if a = 10 {
print " yes.".
set doNothing to 1. // just to make a reason for the braces.
}.
print "if/else, one-liners, true check:".
print "is " + a + " = 10?".
if a = 10
print " yes".
else
print " no".
print "if/else, one-liners, false check:".
print "is " + b + " = 10?".
if b = 10
print " yes".
else
print " no".
print "if/else, multi-line bodies, true check:".
print "is " + a + " = 10?".
if a = 10 {
print " yes".
set doNothing to 1. // just to make the body longer than 1 stmt.
} else {
print " no".
set doNothing to 1. // just to make the body longer than 1 stmt.
}.
print "if/else, multi-line bodies, false check:".
print "is " + b + " = 10?".
if b = 10 {
print " yes".
set doNothing to 1. // just to make the body longer than 1 stmt.
} else {
print " no".
set doNothing to 1. // just to make the body longer than 1 stmt.
}.
print "nested if/else, one-liners, hits first term:".
print "what is " + a + " ?".
if a < 15
print " less than 15".
else if a < 20
print " in the range [15,20)".
else if a < 30
print " in the range [20,30}".
else
print " >= 30".
print "nested if/else, one-liners, hits third term:".
print "what is " + b + " ?".
if b < 15
print " less than 15".
else if b < 20
print " in the range [15,20)".
else if b < 30
print " in the range [20,30}".
else
print " >= 30".
print "nested if/else, one-liners, hits final else:".
print "what is " + c + " ?".
if c < 15
print " less than 15".
else if c < 20
print " in the range [15,20)".
else if c < 30
print " in the range [20,30}".
else
print " >= 30".
print "nested if/else, mix of one-line and braces, hits middle term:".
print "what is " + b + " ?".
if b < 10
print " less than 10.".
else if b < 30 {
print " in the range [10,30}".
set doNothing to 1. // pointless line to force need for braces.
} else {
print " >= 30".
set doNothing to 1. // pointless line to force need for braces.
}.
It occurs to me that expanding a thing like that into a very complex long script full of WHENs, LOCKs, FORs, UNTILs, Program calls, and so on might be a good idea for trying to exercise a bit of everything in the language, so that after a change you could run the test and compare the results to the results of a previous run, and look at the diff between them to make darn sure that any change you see is a change you meant to see.
Furthermore, when a new bit of syntax is added, you could update the regression test script to add checks for your new thing. Then when you diff it, you expect those changes for your new syntax, but are also making sure nothing previously working became broken.
If people think this is a good idea I would be happy to try to work out a starting version of it. The idea would be that it would be added to the github repository and using it to check any language feature update would be a standard practice people should get in the habit of. It would be a thing you could do as part of a check to see whether or not to accept a pull request.
Here's a few design bullet-points about how I picture it working:
LOG
commands). This is so you'd have a file of the output on your computer that you can look at with an external diff tool.LOG
command ends up giving you a disk file, rather than a bunch of lines in a section of your persistence file.Hello,
I encounter this when running exenode. It seems to be a c# error.
// execute maneuver node
set nd to nextnode.
print "T+" + round(missiontime) + " Node in: " + round(nd:eta) + ", DeltaV: " + round(nd:deltav:mag).
print "T+" + round(missiontime) + " Node apoapsis: " + round(nd:orbit:apoapsis/1000) + "km, periapsis: " + round(nd:orbit:periapsis/1000) + "km".
set maxda to maxthrust/mass.
print "T+" + round(missiontime) + " Max DeltaA for engine: " + round(maxda).
set dob to nd:deltav:mag/maxda. // incorrect: should use tsiolkovsky formula
print "T+" + round(missiontime) + " Duration of burn: " + round(dob).
print "T+" + round(missiontime) + " Warping to maneuver...".
run warp(nd:eta - dob/2 - 45).
// turn does not work during warp - so do now
print "T+" + round(missiontime) + " Turning ship to burn direction.".
sas off.
rcs on.
// workaround for steering:pitch not working with node assigned
lock np to R(0,0,0) * nd:deltav.
lock steering to np.
wait until abs(np:pitch - facing:pitch) < 0.1 and abs(np:yaw - facing:yaw) < 0.1.
rcs off.
run warp(nd:eta - dob/2).
wait 3.
wait until abs(np:pitch - facing:pitch) < 0.1 and abs(np:yaw - facing:yaw) < 0.1.
wait 2.
print "T+" + round(missiontime) + " Orbital burn start " + round(nd:eta) + "s before apoapsis.".
// lock steering to node:prograde which wanders off at small deltav
if nd:deltav:mag > 2*maxda {
when nd:deltav:mag < 2*maxda then {
print "T+" + round(missiontime) + " Reducing throttle, deltav " + round(nd:deltav:mag) + ", fuel:" + round(stage:liquidfuel).
// continue to accelerate node:deltav
set np to R(0,0,0) * nd:deltav.
}
}
set tvar to 0.
lock throttle to tvar.
until nd:deltav:mag < 1 and stage:liquidfuel > 0 {
set da to maxthrust*throttle/mass.
set tset to nd:deltav:mag * mass / maxthrust.
if nd:deltav:mag < 2*da and tset > 0.1 {
set tvar to tset.
}
if nd:deltav:mag > 2*da {
set tvar to 1.
}
print "Throttle: " + round(tset) at (0,29).
print "DeltaA: " + round(da) at (20,29).
print "Node DeltaV: " + round(nd:deltav:mag) at (0,30).
print "Apoapis: " + round(apoapsis/1000) at (0,31).
print "Periapis: " + round(periapsis/1000) at (20,31).
}
// compensate 1m/s due to "until" stopping short; nd:deltav:mag never gets to 0!
if stage:liquidfuel > 0 {
wait 1/da.
}
lock throttle to 0.
unlock steering.
remove nextnode.
print "T+" + round(missiontime) + " Burn complete, apoapsis: " + round(apoapsis/1000) + "km, periapsis: " + round(periapsis/1000) + "km".
print "T+" + round(missiontime) + " Fuel after burn: " + round(stage:liquidfuel).
COPY BOGUSNAME FROM 1.
When BOGUSNAME is a non-existant file, the above line does nothing, but produces no complaint or error message.
Once you do this:
set target to "some ship".
Then the only way to unset the target from then on is with manual user clicks.
You cannot set the target to a bogus ship name string, nor does "unset target" cause the target to be deselected.
I remember that one used to be able to set
THROTTLE and STEERING. Why is this not the case any longer?
A separate thread for coding standards. In general.
When I run the code shown below, I receive the error mentioned in the title. However, the parameter is a number because it is logged at a number in "debug."
As a side note, the "line zero errors" are frustrating because you have to guess where the error is coming from.
CLEARSCREEN.
set isLanded to 0.
set isFinal to 0.
set lastTime to TIME.
set dt to 1.
// Navigation constants
set threshW to latlng(-0.04022641,-74.71819).
set targW to latlng(-0.04059032,-74.67182).
set targElev to 67.
set threshE to latlng(-0.04179348,-74.49593).
set vor1 to latlng(-0.035,-76.5).
set glideslope to -8.
set glideslopevari to 1.
// Persistent private variables
set climbuUuerrSum to 0.
set climbuUulastErr to 0.
set climbuUulastInput to 0.
set speeduUuerrSum to 0.
set speeduUulastErr to 0.
set speeduUulastInput to 0.
set speeduUuSMOOTHuUuStm1 to 0.
set speeduUuSMOOTHuUuXtm1 to 0.
set pitchuUuerrSum to 0.
set pitchuUulastErr to 0.
set pitchuUulastInput to 0.
set pitchuUuSMOOTHuUuStm1 to 0.
set pitchuUuSMOOTHuUuXtm1 to 0.
UNTIL isLanded = 1 {
set manageAlt to 0.
set manageClimb to 0.
set manageSpeed to 0.
set managePitch to 0.
// Intermediate variables
set tClimb to 0.
set tAlt to 0.
set tSpeed to 0.
set tPitch to 0. // Both an intermediate and final variable
// Final variables
set tThrust to 0.
set tPitch to 0.
set tHeading to 0.
// <main>
if isFinal = 0 {
set tAlt to 2500.
set manageAlt to 1.
set tSpeed to 150.
set manageSpeed to 1.
if (vor1:distance > 2500) {
set tHeading to 90.
set dr to vor1:distance - 30000. // Target arc: 30km
set dH to dr*(90/5000).
if dH < -90 {
set dH to -90.
}.
if dH > 90 {
set dH to 90
}.
set dH to dH + 90.
set tHeading to vor1:HEADING + dH.
if (vor1:HEADING > 45 AND vor1:HEADING < 135) {
set tHeading to vor1:HEADING.
}.
}.
if (vor1:distance <= 2500) {
set tHeading to 90.
if (vor1:HEADING > 45 AND vor1:HEADING < 135 AND tAlt > 2000 AND tAlt < 3000) {
set isFinal to 1.
}.
}
}.
if isFinal = 1 {
set tPitch to 5.
set managePitch to 1.
set hdist to ((altitude-targElev)^2+(targW:distance)^2)^0.5. //because arctan is busted.
set slope to (-1)*arccos(targW:distance/hdist).
set baseClimb to tan(glideslope)*SURFACESPEED.
set diff to (targW:bearing-threshE:bearing).
if slope < (glideslope-glideslopevari) {
// Too low
set dS to (glideslope-glideslopevari) - slope.
set tClimb to baseClimb + 3*dS.
set manageClimb to 1.
}.
if slope > (glideslope+glideslopevari) {
// Too high
set dS to slope - (glideslope+glideslopevari).
set tClimb to baseClimb - 3*dS.
set manageClimb to 1.
}.
if slope < (glideslope+glideslopevari) AND slope > (glideslope-glideslopevari){
set tClimb to baseClimb.
set manageClimb to 1.
}.
if targW:bearing > 0.5 {
// Too far left
set tHeading to 90 + 2*abs(diff).
}.
if targW:bearing < -0.5 {
// Too far right
set tHeading to 90 - 2*abs(diff).
}.
if abs(targW:bearing) < 0.5 and abs(diff) < 0.25{
set tHeading to 90.
}.
if (ALTITUDE-targElev) < 50 {
set tClimb to -5.
set manageClimb to 1.
set tHeading to 90.
}.
if (ALTITUDE-targElev) < 1.9 {
set isLanded to 1.
}.
}.
// </main>
// Achieves tPitch using tSpeed
if managePitch = 1 {
set realPitch to tPitch.
if manageClimb {
set realPitch to (0.01 * error + 0.001 * climbuUuerrSum - 0.001 * dInput)/spdRatio. // Sloppy way of getting (close to) actual pitch
}.
set error to tPitch - realPitch.
set pitchuUuerrSum to (error * dt) + pitchuUuerrSum.
set dInput to (realPitch - pitchuUulastInput) / dt.
set pitchuUulastErr to error.
set pitchuUulastInput to realPitch.
set tSpeed to 0.1 * error + 0 * pitchuUuerrSum - 0 * dInput.
set smoothingFactor to 0.9.
set invSmoothingFactor to 1 - smoothingFactor.
set St to pitchuUuSMOOTHuUuXtm1 * smoothingFactor + invSmoothingFactor * pitchuUuSMOOTHuUuStm1.
set pitchuUuSMOOTHuUuStm1 to St.
set pitchuUuSMOOTHuUuXtm1 to tSpeed.
set tSpeed to St.
if (tSpeed < 70) {
set tSpeed to 70.
}.
if (tSpeed > 300) {
set tSpeed to 300.
}.
}.
// Achieves tAlt using tClimb
if manageAlt = 1 {
set spdRatio to SURFACESPEED/100.
set maxClimb to spdRatio*100.
set maxDescent to spdRatio*-100.
set tClimb to (tAlt - ALTITUDE) * 5.
if tClimb > maxClimb {
set tClimb to maxClimb.
}.
if tClimb < maxDescent {
set tClimb to maxDescent.
}.
set manageClimb to 1.
}.
// Achieves tClimb using tPitch
if manageClimb = 1 {
set spdRatio to SURFACESPEED/100.
set error to tClimb + VERTICALSPEED.
set climbuUuerrSum to (error * dt) + climbuUuerrSum.
set dInput to (-1*VERTICALSPEED - climbuUulastInput) / dt.
set climbuUulastErr to error.
set climbuUulastInput to -1*VERTICALSPEED.
set tPitch to (0.01 * error + 0.001 * climbuUuerrSum - 0.001 * dInput)/spdRatio.
}.
// Achieves tSpeed using tThrust
if manageSpeed = 1 {
set error to tSpeed - SURFACESPEED.
set speeduUuerrSum to (error * dt) + speeduUuerrSum.
set dInput to (SURFACESPEED - speeduUulastInput) / dt.
set speeduUulastErr to error.
set speeduUulastInput to SURFACESPEED.
set tThrust to 0.1 * error + 0 * speeduUuerrSum - 0 * dInput.
set smoothingFactor to 0.9.
set invSmoothingFactor to 1 - smoothingFactor.
set St to speeduUuSMOOTHuUuXtm1 * smoothingFactor + invSmoothingFactor * speeduUuSMOOTHuUuStm1.
set speeduUuSMOOTHuUuStm1 to St.
set speeduUuSMOOTHuUuXtm1 to tThrust.
set tThrust to St.
if (tThrust < 0) {
set tThrust to 0.
}.
if (tThrust > 1) {
set tThrust to 1.
}.
}.
if (tPitch > 45) {
set tPitch to 45.
}.
if (tPitch < -45) {
set tPitch to -45.
}.
//set tHeading to tHeading - 90.
UNTIL tHeading <= 360 {
set tHeading to tHeading - 360.
}.
UNTIL tHeading > 0 {
set tHeading to tHeading + 360.
}.
// Set all of the values
set aThrust to tThrust.
set aHeading to tHeading.
set aPitch to tPitch.
print "Pitch: " + aPitch + " " at (5,1).
print "Heading: " + aHeading + 90 + " " at (5,2).
print "Thrust: " + aThrust + " " at (5,3).
print "Steering: " + steering + " " at (5,4).
print "Vor1 heading: " + vor1:heading + " " at (5,5).
print "Prograde: " + prograde + " " at (5,6).
lock THROTTLE to aThrust.
//lock STEERING to HEADING(aHeading, aPitch).
set aCorrect to -90 + aPitch.
LOG aCorrect to debug.
lock steering to UP + R(tHeading,aCorrect,270).
// Calculate the time loop
set dt to TIME - lastTime.
set lastTime to TIME.
}.
set THROTTLE to 0.
set STEERING to HEADING(90, 3).
BRAKES ON.
The value of SHIP:SOLIDFUEL doesn't return zero when there's no solid fuel. Instead it says "suffix SOLIDFUEL not found on object" and bombs out.
I was trying to modify my ascent script, which used to only work if all the engines are liquid fueled, to handle working when there may or may not be solid boosters attached, but it's currently a mess to try to write the script because of this problem. If I try to query for whether or not solid fuel exists on the ship outside the current stage, the script bombs out while attempting the query.
Note, it does seem to work for STAGE:SOLIDFUEL. That does return zero. But SHIP:SOLIDFUEL, which seems to be taking its value from the KSP resource , is finding no such resource when querying for the SOLIDFUEL resource and thus producing that error.
Note that if you have solid boosters that have just run out and you haven't detached them yet, then SHIP:SOLIDFUEL does return zero. But the moment you decouple them, then SHIP:SOLIDFUEL will bomb out if you try to query it. It seems that as long as a part is attached to the ship that is capable of holding a resource but has run out, then querying for that resource will return zero. (i.e. empty fuel tanks or spent batteries) But the moment the part is no longer attached, then querying for that resource will error out.
i'm not sure how to fix this cleanly, since the KSP API itself seems to treat "this type of resource is currently not part of THIS ship" identically to "this type of resource cannot be part of ANY ship because I've never heard of it." Therefore if you change it to return zero when the resource is not found, then all misspelled resources would return zero as well and that's not desirable.
Rant: This is part of the reason I think it's important to expose some means for the script programmer to perform a boolean check against NULL and NaN conditions, and allow the script writer the means to handle these sorts of conditions instead of making them crash the script. Because there's no exception try/catch in the language, and I agree that implementing it would make the language too complex for entry level people, these conditions end up being impossible to work around.
If ship:solidfuel returned null, and there was a means to check isnull(ship:solidfuel) then I could handle the problem in my own script. Similarly with math functions that return NaN like taking the arccos of a number larger than 1. it's impossible to ever check for this condition currently. When a math function returns NaN you can't ever check for that case in kOSscript and instead you just have a variable that will cause a crash when you try using it.
I had some scripts that used variable holding large constants in 0.11 that now no longer work in the 0.12.P3 pre-release.
SET const_g to 6.674 *10 ^ (-1 * 11). //now returns 0
SET body_mass TO 5.2915793 * 10^22. //now returns a large negative number
I found out that I can do
SET const_g TO 6.674E-11.
SET body_mass TO 5.2915793E+22.
I do also know there is now CONSTANT():G and the body list to get both of these numbers but this might still be an issue that should be looked at.
print "starting.".
set var to "set value".
print "set var is: " + var.
lock var to "locked value".
print "locked var is: " + var.
Result:
starting.
Variable var* is not defined
Instruction 10
When going through and parsing the script ahead of time, kOS sets up conditions such that if var will at any point in the script be used in a lock command it cannot ever be used in a set command elsewhere in the script (even at an earlier point in the script).
(suggestion for fix: if it's going to be a lock such that it must be a function returning a value, then when the 'set' is encountered make kOS translate that kosscript code into this k-risc code:
step 1 - evaluate expression into a temp value.
step 2 - lock variable to that constant temp value.
This way the variable always is a 'lock' even when used as a 'set'. The 'lock' is locking to a value that was calculated just once instead of each time - so it behaves like a 'set'.)
When launching from Kerbin, the time warp mode sometimes remains stuck in physics mode even after leaving the atmosphere and cutting the throttle. I believe this is a bug in the stock game, however when flying manually it is easily worked around by clicking the 1x (normal speed) button manually, causing the game to switch to rails mode if it didn't do so automatically.
However, KOS seems to be unable to trigger the switch from physics warp to rails warp. As a result, my auto-warping scripts tend to crash a lot with the message,
Array index is out of range.
Which occurs when a script tries to set WARP to a level higher than 3 while in physics mode. (WARP levels 4-7 are only valid when in rails mode.)
I have inserted gratuitous commands like
set WARP to 0.
WAIT 1.
into my scripts to try and force the game to switch to rails mode if available, but unlike manually clicking the 1x button, this doesn't seem to help when a script does it.
(Strangely, I think it may actually be the act of moving the mouse over the UI which causes the game to recheck if rails warp is available, rather than clicking on the 1x button, specifically.)
Any one of these changes would allow me to solve my current problem, but the most comprehensive solution on KOS' part would be to implement 1, 2, and 3 combined. I strongly suspect that 4 is possible to do in a plugin, but I understand that is outside of the scope of the KOS project.
C's FOR loop is quite versatile. A lot of other languages have loops that sound nicer in English when read out ("for x from 1 to 10 step 2" for example), but what they gain in looks and legibility, they lose in versatility. They constrain the type of loop you can have. It must have exactly one loop counter variable, it must increment by a value known ahead of time, with an endpoint based upon a greater-than or less-than check which isn't appropriate for some algorithms where the counter isn't a number, and so on.
What I'm considering is a loop with the versatility of the C FOR loop , with the same 3 parts to it (do this when starting, check this to see when to stop, do this to increment), but done in a way more consistent with kosscript's "special" style of syntax.
I propose this syntax:
FROM
init UNTIL
end-check GOING
incrimenter {
… }
.
Or the same thing by overloading the FOR syntax to also accept:
FOR
init UNTIL
end-check GOING
incrimenter {
… }
.
(I'm not sure which I prefer. "FOR" is more traditional for programming, and also used by the list iterator loop already, but "FROM" flows better in the way it sounds in English and they way I've written it.)
examples:
FOR x=0 UNTIL x>10 GOING x=x+1
{
print x.
}.
I wold propose that the expressions init, end-check, and incrimenter actually be designed to be any block of code, so they could include curly braced complex sections, like so:
// Iterate with x counting up while y counts down and they meet in the middle:
FOR {x=0. y=100.} UNTIL x >= y GOING {x=x+1. y=y-1}
{
… stuff ...
}.
It should be instantly familiar to experienced programmers, while at the same time being easy to understand for first-timers because it describes the clauses and what they mean.
The implementation of parsing it is very simple, I think. It's just that you take anything of the form:
FOR A UNTIL B GOING C { body }.
And make it behave like so:
A.
UNTIL B {
body.
C.
}.
Most of the routines to do this are already there. It's just a matter of linking them together.
When using the LOCK STEERING style of control, when the program ends the system gives control back to the player again, even if "UNLOCK STEERING" was never explicitly called. This is very useful when programs crash or stop from an exception before reaching the intended endpoint where the UNLOCK command would be.
But when using the SET SHIP:CONTROL... style of control, it does not do this. When the program ends the player still cannot manually control the ship until issuing the "SET SHIP:CONTROL:NEUTRALIZE to True" command at the interactive terminal.
The control should probably be given back to the player automatically on exiting the (outermost nested) program just like it is for the LOCK STEERING technique.
I think the ability to do the following would be useful:
if isNULL( expr ) {
// stuff
}
if isNaN( expr ) {
// stuff
}
Only if every place in the kOS C# code that could potentially return NaN or NULL to the script manages to check for every case where that could happen and trap it and turn it into a different result would such functions be useless. (And even then I would argue that doing that trap is the wrong solution as it hides relevant data from the script programmer. There is a big difference between a valid number versus a NaN and a big difference between a valid object with zeroed-out data versus a NULL).
For example, you try to look at what your next encounter is. But there is no next encounter. NULL is the sensible way to report that, but currently returning a NULL to the script leaves the script programmer helpless to do anything to detect it and just means their code will crash when they try using the result.
There is also a difference between "your fuel tanks are empty" versus "you have no fuel tanks", so returning zero for cases where a resource is invalid and missing might not actually be the correct answer (see #5).
I discovered this when I was trying to print out a lot of stuff to test the problem I was having with a throttle of infinity (which used to work in old versions of kOS and just became a throttle of 1.0 effectively). When trying to diagnose that problem I ended up trying a lot of stuff at the interactive mode terminal that accidentally exposed this other problem:
Examine this output from the interactive terminal:
lock var to 0.5.
print var.
0.5
lock var to 0.4.
The given key was not present in the dictionary.
set var to 0.4.
The given key was not present in the dictionary.
set anotherVar to 5
The given key was not present in the dictionary.
copy testprog from 0.
The given key was not present in the dictionary.
This only happens in interactive mode. Once I try to "lock var to ..." a second time for a var that's been locked already, all statements parsed from then on seem to be broken, for all variables, even new ones, and any statement.
The same problem does not happen when doing it inside a program file. The following program runs just fine:
lock var to 0.5.
print var.
lock var to 0.4.
print var.
lock anotherLock to 5.0.
print anotherLock.
set anotherVar to 6.0.
print anotherVar.
Does this have something to do with the difference between how the parser behaves when it can see the upcoming future statements ahead of time (in a program file) versus when it can't (in interactive mode)?
My program is copied to a local volume and then executed while still in range. It waits untill 60 seconds before ETA:APOAPSIS before turning off SAS and locking steering to prograde. Then 30 seconds before ETA:APOAPSIS it turns SAS on and locks throttle to 1. For some reason neither of the lock commands seem to work while out of range. SAS is toggled just fine and a visual aspect of the program continues to work.
(This is a repeat of an issue that was just raised on the old KOS project just before erendrake and marionoapp merged their work into this project.)
1: Add a boolean unary negation operator, as in:
If not x = 2 { ... }.
or
if ! x = 2 { ... }.
2: Also the "not equal" operator seems to be missing from the otherwise complete list of comparators:
Implement either like this:
if x <> 2 {...}.
or like this
if x != 2 {...}.
I intend to have this feature that I can use for myself because debugging math problems is a pain when you can't see the XYZ coordinate directions and can't tell what your vectors are really doing. If I implement it for myself, do you think it would be useful to put into the general project?
Read this:
What I'm picturing is allowing users to issue a commands like so:
SET myRedArrow to DRAWARROW( Mun:Position , 1000000 , 1, 0, 0 ).
// argument 1 is any vector of any kind. In this case the mun's position relative to the craft.
// the 1,0,0 is the RGB color to draw the arrow.
// the '1000000' is the drawing scale i.e. at 1 million, a vector with magnitude 5 million woud be drawn as a 5-meter long line in space)
UNSET myRedArrow. // causes the arrow to disappear from the screen.
This allows users to actually SEE what the vectors are, and get a much better visual handle on what they're doing. The purpose is for helping them (and me) debug scripts.
An alternate and perhaps simpler syntax for the same idea would be to add suffix properties to kOS's Vector class, so that any Vector can have its drawing properties tweaked like so:
set myVec to V(1000,201,401.1).
set myVec:drawRed to 1.0.
set myVec:drawGreen to 0.0.
set myVec:drawBlue to 0.0.
set myVec:drawScale to 50.
set myVec:drawShow to true. // make it appear.
set myVec:drawShow to false. // make it disappear. All vectors start with drawShow set to false by default.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.