Closed beamer159 closed 2 years ago
Before I get started answering all the questions for the 5 scenarios, I will provide some info about how Tank Royale works regarding the Bot APIs. The Bot APIs have been created to make life easier as a bot developer, as you could alternatively send and receive messages over websockets in a more raw way using the schema/protocol. But of ocurse the Bot APIs should be the obvious choice for most developers. 🙂
The run()
method should be seen the same way an ordinary main()
method, except that main()
is used for startup code (called only once by startup script), and run()
is being called for each round by the game.
Note: As soon as you leave the run() method, your bots control over execution is done like a normal program being run inside a main()
method. And the game is stopping event processing in the background, even though it sometimes fails, if the run()
method goes not terminate. Hence, running code in an infinite loop as you do in scenario 5 is really bad practise, as there is no way to stop the thread when it runs forever.
Hence, avoid infinite loops and use e.g. while (isRunning()) // do stuff
as the game will be able to stop the loop.
All that go()
does it sending an intent with target speed, turning rates etc. to the server. It is an intent as it is a request, not a command that must be obeyed. If e.g. the bot sets the target speed to e.g. 1000, the game will truncate it to 8.
If for some reason the intent is not sent to or received by the server, the server will consider this a skipped turn, and the bot will have no update in movement etc. for that turn, and the game will continue to use the previous values being sent, i.e. the last received instructions for the individual turn rate, target speed etc. A new intent basically overwrittes the previous set values.
With the above in mind, we are ready to examine the scenarios.
setTurnRight(Double.POSITIVE_INFINITY);
forward(Double.POSITIVE_INFINITY);
Is basically the same as:
setTurnRight(Double.POSITIVE_INFINITY);
setForward(Double.POSITIVE_INFINITY);
go()
forward()
is a blocking call that calls setForward()
+ go()
one to many times depending on the input value and distance remaining.
setForward(Double.POSITIVE_INFINITY);
turnRight(Double.POSITIVE_INFINITY);
Is basically the same as:
setForward(Double.POSITIVE_INFINITY);
setTurnRight(Double.POSITIVE_INFINITY);
go()
setForward(Double.POSITIVE_INFINITY);
setTurnRight(Double.POSITIVE_INFINITY);
Obviously, this does nothing in itself, as go() is not being called, and hence the intent is not being sent to the server.
setForward(Double.POSITIVE_INFINITY);
setTurnRight(Double.POSITIVE_INFINITY);
go();
The bot should move in a circle. I guess this is a bug, but in a special scenario where it is the very first round and turn. But nevertheless, a bug.
setForward(Double.POSITIVE_INFINITY);
setTurnRight(Double.POSITIVE_INFINITY);
while (true) {
go();
}
The infinite loop is BAD practice here, and the cause of your trouble. Robocode cannot stop your thread, and will wait for it to stop before starting a new one (to avoid multiple threads to do different things on the bot causing serious raise conditions that are hard to cope with).
Try this instead:
setForward(Double.POSITIVE_INFINITY);
setTurnRight(Double.POSITIVE_INFINITY);
while (isRunning()) {
go();
}
Thanks for the tip about:
Exception in thread "Thread-28" java.lang.IllegalArgumentException: -Infinity is not a valid double value as per JSON specification. To override this behavior, use GsonBuilder.serializeSpecialFloatingPointValues() method.
I will fix this too of course. 🙂
Regarding scenario 4, I am not sure this is a bug. It seems that these two behave the same, which is consistent:
setForward(Double.POSITIVE_INFINITY);
setTurnRight(Double.POSITIVE_INFINITY);
go();
Compared to:
setTurnRight(Double.POSITIVE_INFINITY);
setForward(Double.POSITIVE_INFINITY);
go();
Behaves the same way.
Should scenarios 1, 2, and 4 all behave the same?
@beamer159
Scenario 1, 2, 4 are not entirely the same. In the end it depends on the blocking call, which will wait (block) until its condition it met, before continuing to process the next instruction, if it exists. And since they are waiting for an infitite value to be reached, this will never happen, and they will wait forever - at least for scenarios 1 and 2.
With scenario 4, we only call a single go()
, which sends the intent to the server (once), and then immidiately moves to the next instruction, if any. But it will not hang until the infinite value is reached. 🙂
The behavior is described in the code with the BotInternals class. Here it is for the Java API: https://github.com/robocode-dev/tank-royale/blob/master/bot-api/java/src/main/java/dev/robocode/tankroyale/botapi/internal/BotInternals.java
Have a look at forward, turnLeft, turnGunLeft, turnRadarleft. . 🙂
You may already know this, but the following code moves the bot quite slowly.
setForward(Double.POSITIVE_INFINITY);
go();
This works better though.
setForward(Double.POSITIVE_INFINITY);
while (isRunning()) {
go();
}
@beamer159
I believe that setForward(Double.POSITIVE_INFINITY)
should set the target speed to the max. speed = 8. Hence the bot should accelerate with 1 unit/turn when calling setForward with an infite value.
A fix is available with the next version.
@beamer159
Some of the issues observed with #20 might also might some of the bugs seen in this one with release 0.13.3.
And note that the first turn in the first round migth skip the first go() instruction. So if run() is only calling a single go() before it exists, then the bot might not move. So make sure to have a couple of go's if you are not using a loop.
@beamer159
I believe that
setForward(Double.POSITIVE_INFINITY)
should set the target speed to the max. speed = 8. Hence the bot should accelerate with 1 unit/turn when calling setForward with an infite value.A fix is available with the next version.
You may already know this, but the following code moves the bot quite slowly.
setForward(Double.POSITIVE_INFINITY); go();
This works better though.
setForward(Double.POSITIVE_INFINITY); while (isRunning()) { go(); }
This is also fixed with release 0.13.3
I fixed the issues we have discussed. 😊
I want to make a bot that moves in a circle. I coded this in multiple ways that I assumed would behave the same. However, they each have different behaviors. Some of these different behaviors may be intentional, I am just curious about them.
1.
This behaves how I would expect. The bot moves in a circle. If it hits an obstacle, it continues attempting to move in a circle. All is well and good
2.
This behaves similar to 1; the bot moves in a circle. However, if it hits an obstacle, it stops moving forward and spins in place. It seems like
forward
maintains distance remaining when the bot runs into something, butsetForward
does not3.
The bot does not move at all. I expected this because I do not call
go
, and the documentation says thatgo
must be called every turn if using these functions. Although this seems obvious, I thought there might be a chance thatgo
is called automatically after therun
function completes.4.
The bot moves in a tiny circle. Due to my misunderstanding of
go
, I was expecting this to either move for a single turn and then stop, or move similar to 1 and 2. However, the forward speed is much smaller than these previous cases. Additionally, it seems like bumping into obstacles does not eliminate its forward movement like 2, even thoughsetForward
is used here as well.I should also mention when I was testing this case, I threw the following exception once at the beginning of a run. This exception seems to occur rarely for some reason, and I think it could have happened during any of these cases.
5.
The bot spins in place. Also, when I choose to restart the battle, the battle does not begin. To retry, I have to close the bot process and start it anew. I actually expected this to be the correct way to code a circle bot. The documentation for
go
says that it must be called every turn, so putting it in a while loop seemed the best way to ensure this. I thought ifgo
is not called, actions would not be sent to the server, hence why I thought case 4 might have the bot only move for a single turn. Currently though, it appears that puttinggo
in a loop with nothing else causes the most problems.