kaggle / kaggle-environments Goto Github PK
View Code? Open in Web Editor NEWHome Page: https://www.kaggle.com/competitions
License: Apache License 2.0
Home Page: https://www.kaggle.com/competitions
License: Apache License 2.0
Halite crashes when two ship collide with the same enemy shipyard on the same turn.
See notebook.
[Edit: renamed notebook]
The HTML replay files will get longer and longer regardless of if you remake or reset the actual env.
Empty arrays are appended in the "logs" part of the HTML files code, creating HTML files with hundred thousands lines.
Hi,
when creating smaller boards (like 5x5) some tail can have more than 500 halite on it.
For example:
board_size = 5
environment = make(
"halite", configuration={"size": board_size, "randomSeed": 1}
)
agent_count = 4
environment.reset(agent_count)
yields
{... 'halite': [267, 2, 3929, 2, 267, 0, 808, 0, 808, 0, 682, 11, 297, 11, 682, 0, 808, 0, 808, 0, 267, 2, 3929, 2, 267], ...}
I'm attempting to replay earlier games to see how same agent does. I keep track of list of observations that I get from step:
obs = env.step([action0, action1])
Then I set it on steps
when I build the environment:
prev_steps = json.load(open('replay-score-at-1799.json'))
env = make(
'football',
configuration={
# 'save_video': True,
# 'running_in_notebook': False,
'action_set': 'default',
"scenario_name": "11_vs_11_kaggle",
# 'football_video_path': 'football.webm',
'number_of_left_players_agent_controls': 1,
'number_of_right_players_agent_controls': 1,
},
steps=prev_steps[:1750],
debug=True)
It looks like the state of the environment is correct:
obs = env.state # right after initializing
print("Steps left on load:", obs[0]['observation']['players_raw'][0]['steps_left'])
print("env steps length:", len(env.steps))
# output is:
# Steps left on load: 1252
# env steps length: 1750
However when I take the first 'step' from env.step
:
obs = env.state # right after initializing
action0 = ...
action1 = ...
print("actions:", action0, action1, action0 + action1)
obs = env.step([action0, action1])
... it resets the environment:
actions: [5] [1] [5, 1]
Staring a new environment 773984e8-8c98-4ed9-a03b-9c21bce6aeec: with scenario: 11_vs_11_kaggle
What am I doing wrong?
Installed the latest version of kaggle-environments
using
pip install kaggle-environments
During import
from kaggle_environments.envs.hungry_geese.hungry_geese import Observation, Configuration, Action, row_col, translate
getting following error:
ModuleNotFoundError: No module named 'kaggle_environments.envs.hungry_geese'
When you play ConnectX the UI always draws the winning line as length 4 even when you play with inarow set to something other than 4. Try this until you see a win, the winning line should be length 5, but it is length 4.
from kaggle_environments import make
config = {'columns': 9, 'rows': 8, 'inarow': 5 }
env = make("connectx", config)
env.run(["random", "random"])
env.render(mode="ipython", width = 800, height = 600)
Change this line with a hard-coded array index set to [3] to [inarow - 1]?
This is a bug that no one will notice in the current version where inarow is always 4, but it is a bug.
In a fresh virtual environment, after installing this package:
$ pip install kaggle-environments
I get a ModuleNotFoundError
:
>>> from kaggle_environments import make, evaluate
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/usr/local/lib/python3.9/site-packages/kaggle_environments/__init__.py", line 17, in <module>
from .agent import Agent
File "/usr/local/lib/python3.9/site-packages/kaggle_environments/agent.py", line 18, in <module>
import requests
ModuleNotFoundError: No module named 'requests'
This is because the requests
dependency is not in the install_requires
in the package's setup.py
. According to the docs,
install_requires is a setuptools setup.py keyword that should be used to specify what a project minimally needs to run correctly. When the project is installed by pip, this is the specification that is used to install its dependencies.
Therefore, the dependencies for kaggle-environments
(which are listed in the requirements.txt
) should be in the setup.py
install_requires
field. One common way of doing this is just to read the requirements.txt
line by line in setup.py
, even if that is strictly speaking incorrect.
Hi there,
Like the title says it looks like the default behaviour for rendering is not quite working. The following code does not print anything.
from kaggle_environments import make, evaluate, utils
env=make("connectx", debug=True)
env.render()
From the documentation it looks like this should print out the state of the game in ASCII. It looks to be an issue with the core render function rather than the connectx environment as other environment types exhibit the same behaviour.
Hi,
in halite-rules (https://www.kaggle.com/c/halite/overview/halite-rules) is says:
In the game code positions are given as serialized list where each cell is an integer calculated by the formula:
[position = row * 21 + column]
The function "to_index" do:
return (size - self.y - 1) * size + self.x
Correct would be:
return self.y * size + self.x
Same for function "from_index":
y, x = divmod(index, size)
return Point(x, (size - y - 1))
Correct would be:
y, x = divmod(index, size)
return Point(x, y)
The "Manual Play" section of https://github.com/Kaggle/kaggle-environments/blob/master/kaggle_environments/envs/connectx/connectx.ipynb says:
env = make("battlegeese", configuration={"agentExec": "LOCAL"})
Why are you playing "battlegeese" in the ConnectX notebook?
I think I've found a bug when using the Board::next() function when there is a fleet on the edge of the map.
Below is a test I wrote to reproduce, looks like the code that handles positions / indexes is not quite right. I might have a play and see if I can find the bug. Help appreciated though, this would be the nail in the coffin of my java based approach :(
...
import java.util.List;
import kore.Player;
public class BoardTest {
@Test
public void fleetWrapsAroundTheBoard_northSouth() throws IOException {
Board board = getStarterBoard();
int x = board.size / 2;
int playerId = 0;
Point p1 = new Point(x, board.size);
Fleet f1 = new Fleet("f1", 50, Direction.NORTH, p1, 100.0, "N", playerId, board);
final String f1Id = f1.id;
board.addFleet(f1);
Board nextBoard = board.next();
Player player0 = nextBoard.players[playerId];
Fleet nextF1 = List.of(player0.fleets()).stream().filter(fleet -> fleet.id == f1Id).findFirst().get();
Assert.assertEquals("position.x should not change when wrapping", nextF1.position.x, x);
Assert.assertEquals("position.y should wrap to 0", nextF1.position.y, 0);
}
...
java.lang.ArrayIndexOutOfBoundsException: Index -16 out of bounds for length 961
at kore.Board.getCellAtPosition([Board.java:115]())
at kore.Fleet.cell([Fleet.java:30]())
at kore.Board.addFleet([Board.java:120]())
at test.BoardTest.fleetWrapsAroundTheBoard_northSouth([BoardTest.java:571]())
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke([NativeMethodAccessorImpl.java:62]())
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke([DelegatingMethodAccessorImpl.java:43]())
at java.base/java.lang.reflect.Method.invoke([Method.java:566]())
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall([FrameworkMethod.java:59]())
at org.junit.internal.runners.model.ReflectiveCallable.run([ReflectiveCallable.java:12]())
at org.junit.runners.model.FrameworkMethod.invokeExplosively([FrameworkMethod.java:56]())
at org.junit.internal.runners.statements.InvokeMethod.evaluate([InvokeMethod.java:17]())
at org.junit.runners.ParentRunner$3.evaluate([ParentRunner.java:306]())
at org.junit.runners.BlockJUnit4ClassRunner$1.evaluate([BlockJUnit4ClassRunner.java:100]())
at org.junit.runners.ParentRunner.runLeaf([ParentRunner.java:366]())
at org.junit.runners.BlockJUnit4ClassRunner.runChild([BlockJUnit4ClassRunner.java:103]())
at org.junit.runners.BlockJUnit4ClassRunner.runChild([BlockJUnit4ClassRunner.java:63]())
at org.junit.runners.ParentRunner$4.run([ParentRunner.java:331]())
at org.junit.runners.ParentRunner$1.schedule([ParentRunner.java:79]())
at org.junit.runners.ParentRunner.runChildren([ParentRunner.java:329]())
at org.junit.runners.ParentRunner.access$100([ParentRunner.java:66]())
at org.junit.runners.ParentRunner$2.evaluate([ParentRunner.java:293]())
at org.junit.runners.ParentRunner$3.evaluate([ParentRunner.java:306]())
at org.junit.runners.ParentRunner.run([ParentRunner.java:413]())
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run([JUnit4TestReference.java:89]())
at org.eclipse.jdt.internal.junit.runner.TestExecution.run([TestExecution.java:40]())
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests([RemoteTestRunner.java:529]())
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests([RemoteTestRunner.java:756]())
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run([RemoteTestRunner.java:452]())
Hi,
Recently via #134 (commit 33b6561) a new configuration variable was added: __raw_path__
Unfortunately my agents do strict type-checking, and expects the configuration values to only map to ints and floats. This new "__raw_path__"
string -> string entry is causing my existing agents to error out.
Example agents erroring out:
My agent's logs:
[[{"duration": 0.058914, "stdout": "", "stderr": "Traceback (most recent call last):\n File \"/opt/conda/lib/python3.7/site-packages/kaggle_environments/agent.py\", line 157, in act\n action = self.agent(*args)\n File \"/opt/conda/lib/python3.7/site-packages/kaggle_environments/agent.py\", line 129, in callable_agent\n if callable(agent) \\\n File \"/kaggle_simulations/agent/main.py\", line 18, in my_agent\n return cached_agents[index](obs, config)\n File \"/kaggle_simulations/agent/agent.py\", line 62, in __call__\n last_actions, self.configuration, observation, self.playouts, self.seed, self.verbose\nTypeError: run(): incompatible function arguments. The following argument types are supported:\n 1. (arg0: List[str], arg1: Dict[str, Union[int, float]], arg2: Dict[str, Union[int, float, List[int], List[List[int]]]], arg3: int, arg4: int, arg5: int) -> str\n\nInvoked with: ['NONE', 'NONE', 'NONE', 'NONE'], {'actTimeout': 1.0, 'episodeSteps': 200, 'runTimeout': 1200, 'columns': 11, 'rows': 7, 'hunger_rate': 40, 'min_food': 2, 'max_length': 99, '__raw_path__': '/"}]]
Would it be possible to either remove this before the config is passed to agents or restrict it to not affect existing competitions? I will relax my type-checking code for future submissions, but I don't believe there is a way for me to update/modify my previous submissions.
Thank you!
Could you increase the max_log_length
per step in core.py
and agent.py
? 1024 is too small to log against the rich game state of Lux with hundreds of objects. 2048 or 4096 would be much better. With more than 12 months of Simulation experience you should be able to look at average log size and see that an increase will not be a burden.
for (int i = 0; i < arrStrParts[i].length(); i++)
should be replaced in both functions by :
for (int i = 0; i < arrStrParts.length; i++)
the difference is between the length of an array of String, and the length of String at position i in the array.
When I run the code below:
from kaggle_environments import evaluate
import gc
import objgraph
# Same definitions as "make" above.
environment = "halite"
configuration = {"episodeSteps":21, "size": 21, "randomSeed": 123456}
steps = []
# Which agents to run repeatedly. Same as env.run(agents)
agents = ["random", "random"]
# How many times to run them.
num_episodes = 1
count = 0
while 1:
rewards = evaluate(environment, agents, configuration, steps, num_episodes)
objgraph.show_growth()
print('count %s, objgraph.by_type:'%count,len(objgraph.by_type('list')))
count += 1
And output logs are :
Loading environment football failed: No module named 'gfootball'
function 9630 +9630
dict 5061 +5061
tuple 4441 +4441
weakref 2372 +2372
wrapper_descriptor 2149 +2149
list 1500 +1500
method_descriptor 1387 +1387
getset_descriptor 1349 +1349
builtin_function_or_method 1190 +1190
type 1099 +1099
count 0, objgraph.by_type: 1500
list 1522 +22
Struct 49 +5
weakref 2373 +1
count 1, objgraph.by_type: 1522
list 1544 +22
count 2, objgraph.by_type: 1544
list 1566 +22
count 3, objgraph.by_type: 1566
list 1588 +22
count 4, objgraph.by_type: 1588
list 1610 +22
count 5, objgraph.by_type: 1610
list 1632 +22
count 6, objgraph.by_type: 1632
list 1654 +22
count 7, objgraph.by_type: 1654
list 1676 +22
...
The len(objgraph.by_type('list')
will add 22 same to the "episodeSteps":21 plus one all the time.
Even if I use the gc.collect().
Is this the problem about the python Memory leak? Is there anyway to fix it?
Because I find that when I run the evaluate
function repeatedly for the very long time(such as with the loop of whlie True:
), there are some problems with my memory.
As the title suggested, the reset() method in Halite environment no longer randomize the board. The code I tried are very simple lines (with necessary libraries imported of course):
env = make('halite')
env.reset(4)
And when I repeatedly run env.reset(4)
, I always get the same board. I know clearly it wasn't the case a few weeks ago. Is this change of behavior intentional? If I repeatedly run both lines, it certainly generates a randomized board because the whole environment object is brand new. But I have a lot of codes based on the assumption that the initial state of the board is not fixed for a given kaggle environment object.
I'm aware that there is a recent change on certain codes regarding random seeds, but I do not have time to fully digest the source code yet.
This is pretty important for me because I'm working on RL and my training calls reset() every time expecting a different and randomized board. My training of models has been performing really wierd recently and I found out about this too late. This sudden change of reset() behavior just destroyed all my training works in the previous weeks. The fix is easy, but please enlighten me what actually happened. Perhaps the earlier behavior of randomizing the board was a bug? Or did you change its design recently?
Hi there,
I am having a problem getting the manual play to work.
I get the board to render, but when I click on a column it just says "processing..." and nothing ever happens.
Here's the code:
from kaggle_environments import evaluate, make, utils
env = make("connectx", debug=True)
env.play([None, "negamax"], width=800, height=600)
I have tried using "random" instead of "negamax" and I have also tried switching to second player, but nothing works.
Here are the versions I am using.
Python 3.7.4
kaggle-environments 1.0.13
jupyterlab 2.1.4
ipython 7.13.0
Thank you,
Marshall
I am trying to analyze the behavior/performance of my agents. For this purpose, I developed metrics that I need to calculate. The metrics include:
However, calculating these metrics turns out to be really difficult with the information provided by the environment. Events like mining, depositing and colliding are not explicitly logged anywhere. Instead, you need to calculate them by evaluating the delta between two adjacent steps.
For example, a depositing event occurred when a ship had no action assigned in the previous step and occupied the same board cell as a friendly shipyard. Then, the amount of halite deposited is equal to the ship's halite in the previous step.
I published a small notebook here explaining the necessary transformations. However, this is tedious and prone to errors.
There must be a better way.
Events should be stored in an events
field in the environment.
Update: I've just noticed that the Environment class is shared across all environments. Maybe the better idea would be adding the events
to event.state
.
Events should have at least the following properties:
pos
(int): where did the event occur?type
(str): what kind of event occured?unit_id
(str): what unit triggered the event?Additional properties could be:
halite
(int): how much was halite mined, regenerated or deposited?created_unit_id
(str): what is the unit id of the unit created by the event?This is very similar to how events were tracked in the Halite 3 .hlt
replay file format.
Event types could be:
mine
: A ship has mined a cell.deposit
: A ship has deposited its halite into a shipyard.collide
: A unit has been destroyed in a collision.spawn
: A ship has been spawned from a shipyard.convert
: A ship has been converted into a shipyard.regenerate
: A cell has regenerated some halite.This solution come with some caveats:
data
property to the event that can contain arbitrary JSON (event specific information)If you like this idea, I'd be happy to be involved in the definition of the events and the development. :)
Hi,
I have tried to install Kaggle-environments on Ubuntu 18.04 using pip but when importing the module, ModuleNotFoundError is thrown. I have searched for the module in site-packages and I found "kaggle_environments-0.0.1.dist-info" folder without the main folder "kaggle_environments" that contains the modules. Could someone support me in this matter, please?
Please note that I have successfully installed Kaggle-environments on my windows machine but could not install it on my VM on the Google Cloud Platform.
Regards,
agent.status != "ACTIVE"
) by sending a non-executable action to the interpreter (example):# Create a Shipyard.
if action == "CONVERT":
if player_halite < config.convertCost - ship_halite:
agent.status = "Insufficient halite to convert a ship to a shipyard."
board = [[-1, {}] for _ in range(size ** 2)]
for index, agent in enumerate(state):
if agent.status != "ACTIVE":
continue
# ...
This means that such units should be invulnerable for collisions. I have not tested this behaviour yet, but will try to provide a minimal test case later.
I don't see why the check for an agent's status is necessary in the first place. All units should be taken into consideration when calculating collisions. So the easiest solution would be to remove that condition. But maybe I am missing something? ๐ค
The agent.status
variable is quite confusing. Sometimes it describes whether an agent is still able to play ("ACTIVE", "DONE", "No potential to gather halite remaining."), sometimes it describes that an action could not be performed ("Insufficient halite to convert a ship to a shipyard.", "Shipyard already present. Cannot convert ship."). I personally also don't like that sometimes this is an uppercased code word and sometimes it it a whole sentence.
I suggest that sending invalid actions to the interpreter should not alter the agent.status
attribute. Instead they should be logged. :)
Hi!
Many users (including myself) have started prefixing their issue with the simulation they refer to (e.g . "Halite: ..."). However, this makes it really hard to filter by in the long run.
I believe it would be super useful to add general labels (bug
, feature
, refactoring
, documentation
) and simulation specific labels (connectx
, halite
, ...) to the issue tracker and allow users to tag their issues.
You can read more about labelling issues here.
What do you think?
Here's a simple example that shows unexpected values of obs.step. The value 1 is repeated and the maximum value of step is 1 (or 2?) less than expected. This example uses an episodeSteps value of 10.
from kaggle_environments import make
def echo_step_agent(obs, config):
print(f"Step {obs.step}")
return {}
env = make("halite", configuration={"agentExec": "LOCAL", "episodeSteps": 10}, debug=True)
env.run([echo_step_agent])
And this is the output:
Step 1
Step 1
Step 2
Step 3
Step 4
Step 5
Step 6
Step 7
Step 8
Hi!
You stopped tagging releases from v0.2.1 onwards (see in releases section). Is there any reason for that?
By the way, you could set up a CI service like travis to publish every tag release to PyPI. This way, the repository tags and the PyPI releases are always in sync. You can find an example here.
In the halite rule, "Spawning is resolved in board position order (top left first)".
However, the spawning is not ordered by board position. I have confirmed that the shipyard in pos 224 spawned a ship after spawning by the shipyard in pos 225.
I suspect that conversion order is also wrong.
With version 1.7.3 installed via pip the following code
from kaggle_environments import make, evaluate
import random
def random_agent(obs, conf):
return random.randrange(conf.banditCount)
env = make("mab", debug=True)
env.run([random_agent, random_agent])
res = env.render(mode="ansi")
leads to a str' object has no attribute 'action'
-error.
There are two options to fix the problem.
Either change in core.py line 279 to args = [self.steps, self]
(don't know how that would interact for the other environments)
or use env.steps[i]
in line 135 of mab.py. I will create a PR with the latter option, although I think the first option, if done correctly would lead to a better implementation. Also interesting, that the argument of the renderer
function is called steps
but effectively gets the env.state
object.
Working on creating a non-standard submission for Lux, and whenever I submit an experiment I am getting an error. Downloading the logs from the submission allows me to see the StdErr to debug, but it's truncated causing a lot of the debugging information stack trace to be lost.
Example truncated match output:
[
[
{
"duration": 0.005138,
"stdout": "",
"stderr": "Traceback (most recent call last):\n File \"/opt/conda/lib/python3.7/site-packages/kaggle_environments/agent.py\", line 43, in get_last_callable\n code_object = compile(raw, path, \"exec\")\n File \"/kaggle_simulations/agent/main.py\", line 1\n /kaggle_simulations/agent/main.py\n ^\nSyntaxError: invalid syntax\n\nDuring handling of the above exception, another exception occurred:\n\nTraceback (most recent call last):\n File \"/opt/conda/lib/python3.7/site-packages/kaggle_environments/agent.py\", line 157, in act\n action = self.agent(*args)\n File \"/opt/conda/lib/python3.7/site-packages/kaggle_environments/agent.py\", line 123, in callable_agent\n agent = get_last_callable(raw_agent, path=raw) or raw_agent\n File \"/opt/conda/lib/python3.7/site-packages/kaggle_environments/agent.py\", line 64, in get_last_callable\n raise InvalidArgument(\"Invalid raw Python: \" + repr(e))\nkaggle_environments.errors.InvalidArgument: Invalid raw Python: SyntaxError('invalid syntax', ('/kaggle_simulations/agent/main.py', 1, 1, '/kagg"
}
]
]
Can we increase the maximum length significantly by any chance to better help with debugging?
I propose "automatic unit test by GitHub Actions". It has various benefits.
The Halite interpreter stops interpreting an agent's actions after receiving a "SPAWN" or "CONVERT" action. This remains true, even when the "SPAWN" or "CONVERT" action was not executable (e.g. because of insufficient halite).
From the interpreter
function in kaggle_environments/envs/halite/halite.py
(starting from line 232):
for uid, action in agent.action.items():
# Shipyard action (spawn ship):
if action == "SPAWN":
# ...
break
# ...
# Create a Shipyard.
if action == "CONVERT":
# ...
break
# Move Ship Actions.
# ...
The break
statements cause the loop to stop early, without evaluating the remaining actions.
break
statements? Is there a limitation with regards to how many "SPAWN" and "CONVERT" actions can be executed in a single round? If so, this should be added to the environment rules.Trying to install the git version of this repository with poetry will cause a RuntimeError
because the package version cannot be retrieved.
$ poetry add git+https://[email protected]:Kaggle/kaggle-environments.git
# {'name': 'kaggle-environments', 'version': None, 'install_requires': ['jsonschema >= 3.0.1'], 'extras_require': {}, 'python_requires': '>=3.6'}
[RuntimeError]
Unable to retrieve the package version for /tmp/pypoetry-git-kaggle-environments9qyj8znv
I believe this has to do with a importing the version
from kaggle_environments
in the setup.py
.
When a new ship is spawned, board.ships_by_uid in halite.py gets overridden with the details of this ship instead of the details getting put into the dict: self.ships_by_uid = details
I think this should be self.ships_by_uid[temp_uid] = details
Processing ship collisions before shipyard collisions has re-introduced a bug that looks very similar to #33.
I'm trying to install kaggle-environments
on Windows 10, Python 3.8, and run the example code from the Readme (tic tac toe), but it fails.
The installation works fine but when trying to run the code snippet, I get Failed: football: No module named 'gfootball'
even though it doesn't use gfootball.
On Windows, pip install gfootball
fails so I can't easily install it.
Any way to use kaggle-environments
on Windows without gfootball?
If I ask my ship to move to cell x for instance, the cell won't regenerate. This is due to line 337 of env\halite\halite.py
Is this the expected behaviour? It seems in contradiction with the rule that halite in all cell regenerates.
It seems that an agent that does nothing but sit there with its initial ship will be invincible in terms of collisions. One can usually observe this phenomenon just by running the example notebook code below. When the random agent's ships run into one of the idling ships, they crash and their halite is absorbed by the idling ship.
def do_nothing_agent(obs, config):
return {}
from kaggle_environments import evaluate, make
env = make("halite", debug=True)
env.run([do_nothing_agent, "random", do_nothing_agent, do_nothing_agent])
env.render(mode="ipython", width=800, height=600)
Not sure, but I guess this may be related to this issue.
https://www.kaggle.com/c/hungry-geese/submissions?dialog=episodes-episode-14230623
From step 51, a white goose is frozen without disappearing. I think this is a bug instead of game design. (It should disappear.)
As long as I checked json log of the game, the "status" of the goose was "ERROR"
.
Since interpreter
function doesn't do anything for non-"ACTIVE"
goose, "ERROR"
goose is simply frozen as obstacle.
The interpreter
can replace "goose"
by []
(empty list).
kaggle-environments/kaggle_environments/envs/hungry_geese/hungry_geese.py
Lines 186 to 187 in c3f9e24
According to schemas.json, there are also other non-"ACTIVE"
(nor "DONE"
) status, "INACTIVE"
, "INVALID"
, "TIMEOUT"
, which can come from Environment.step
function. Probably the interpreter
function have to treat them, too.
kaggle-environments/kaggle_environments/core.py
Lines 181 to 202 in 8a1a8d3
Currently, the amount of halite stored in a cell can grow negative.
This is due to an implementation detail in the interpreter.
elif obs.halite[ship_pos] > 0:
collect_halite = max(
1, int(obs.halite[ship_pos] * config.collectRate))
obs.halite[ship_pos] -= collect_halite
At least one unit of halite always gets deducted from the cell. When the cell contains less than 1 unit of halite, this value becomes negative. Interestingly, it then starts growing negative halite exponentially, based on the configuration.RegenRate
.
Thanks to Tom Van de Wiele for identifying the behaviour and adding it to the discussion.
I personally believe it would simplify things dramatically if halite was stored in integers rather than float throughout the game. It also aligns better with my understanding of halite as being an atomic resource. In most comparable resource management games (AoE, StarCraft, Strongold, ...) resources are usually tracked in integers as well.
Hi,
Probably this has been answered somewhere, but I have not been able to find it.
So my question is: is it possible to get this to work properly on pyCharm?
Regards
The starter agent calculates spawnMax for shipyards wrongly:
At turn 30, it changes from 4 to 5. The correct turn to change should be 34 according to the Kore rules:
https://www.kaggle.com/competitions/kore-2022-beta/overview/kore-rules
Minimum reproduction code:
paradite@d809d67
When a new ship is spawned two ships can be in the same shipyard at the same time.
See notebook
[Edit: renamed notebook]
The new halite seeding method can set the initial halite to be negative in some squares.
This is different from the earlier bug where harvest halite could go negative. This is a bug with the initial halite seeds.
I blame this call
np.random.gumbel can return negative numbers. I suspect the author knew that because after the call on line 201 an np.clip on line 203 ensures the value is non-negative. However, the author doesn't perform an np.clip after the call on line 207 which ends up sometimes making some values of halite negative.
See notebook
[Edit: grammar, added notebook]
Wouldn't it be helpful to mention numpy as a requirement for kore_fleets
and halite
? Even though your error message is helpful!
Line 4 in 128e969
Comparing an instance of Point
to another object fails when the other object is not subscriptable or is of a length shorter than 2.
The specific issue I ran into, is comparison with None
.
from kaggle_environments.envs.halite.helpers import Point
p = Point(1, 2)
p == None
Traceback (most recent call last):
File "/usr/lib/python3.7/code.py", line 90, in runcode
exec(code, self.locals)
File "<input>", line 1, in <module>
File "/home/robbe/workspace/kaggle/kaggle-environments/kaggle_environments/envs/halite/helpers.py", line 77, in __eq__
return self[0] == other[0] and self[1] == other[1]
TypeError: 'NoneType' object is not subscriptable
Normally you should use p is None
, which works, instead of p == None
, however this is not always in our own control:
p in [(1, 1), (2, 2), None, (1, 3)]
Traceback (most recent call last):
File "/usr/lib/python3.7/code.py", line 90, in runcode
exec(code, self.locals)
File "<input>", line 1, in <module>
File "/home/robbe/workspace/kaggle/kaggle-environments/kaggle_environments/envs/halite/helpers.py", line 77, in __eq__
return self[0] == other[0] and self[1] == other[1]
TypeError: 'NoneType' object is not subscriptable
Hi,
When using multiple threads to collect experience each thread always get lock on call of the step function.
I have been facing this problem since I updated the API. I tried both halite and football environment.
new_obs, reward, done, info = self.env.step(actions)
Full code:
`class EpisodeCollector(threading.Thread):
n_episode = 0
reward_sum = 0
max_episode = 0
def __init__(self, env: FootEnv, policy: Policy, result_queue=None, replays_dir=None):
super().__init__()
self.result_queue = result_queue
self.env = env
self.policy = policy
self.replays_dir = replays_dir
self.n_episode = -1
def clone(self):
obj = EpisodeCollector(self.env, self.policy)
obj.result_queue = self.result_queue
obj.replays_dir = self.replays_dir
obj.n_episode = self.n_episode
return obj
def run(self):
self.result_queue.put(self.collect(1))
def collect(self, n=1):
n = max(n, self.n_episode)
return [self.collect_() for _ in range(n)]
def collect_(self):
memory = Memory()
done = False
EpisodeCollector.n_episode += 1
obs = self.env.reset()
i = 0
total_reward = 0
state = None
while not done:
actions, state = self.policy.get_action(obs, state=state)
new_obs, reward, done, info = self.env.step(actions[0])
total_reward = reward
# store data
memory.store(obs, actions, reward, done)
if done or i % 100 == 0:
with lock:
print(
f"Episode: {EpisodeCollector.n_episode}/{EpisodeCollector.max_episode} | "
f"Step: {i} | "
f"Env ID: {self.env.env_id} | "
f"Reward: {total_reward} | "
f"Done: {done} | "
f"Total Rewards: {EpisodeCollector.reward_sum} | "
)
print(info)
obs = new_obs
i += 1
EpisodeCollector.reward_sum += total_reward
if self.replays_dir:
with open(os.path.join(self.replays_dir, f'replay-{uuid.uuid4().hex}.dill'), 'wb') as f:
dill.dump(memory, f)
return memory
class ParallelEpisodeCollector:
def __init__(self, env_fn, n_jobs, policy: Policy, replays_dir=None, ):
self.n_jobs = n_jobs
self.policy: Policy
self.envs = []
self.result_queue = Queue()
self.replays_dir = replays_dir
for i in range(n_jobs):
self.envs.append(env_fn(env_id=i))
self.collectors = [EpisodeCollector(env,
policy=policy,
result_queue=self.result_queue,
replays_dir=replays_dir) for env in self.envs]
def collect(self, n_steps=1):
if not n_steps: n_steps = 1
result_queue = self.result_queue
for i, collector in enumerate(self.collectors):
collector = collector.clone()
self.collectors[i] = collector
collector.n_episode = max(1, int(n_steps / len(self.collectors)))
print("Starting collector {}".format(i))
collector.start()
tmp = []
for _ in self.collectors:
res = result_queue.get()
tmp.extend(res)
[collector.join() for collector in self.collectors]
return tmp`
A partial fix to this was made in release 0.2.7, but it is not complete.
In connectx the 'board' portion of the observation is missing when training as player 2. When you try this with halite different parts of the observation are missing.
Here is an example with connectx that shows the problem in 0.2.7:
from kaggle_environments import make
env = make("connectx")
policies = ['random', None]
trainer = env.train(policies)
trainer.reset()
next_obs, reward, done, _ = trainer.step(0)
print(next_obs)
displays
{'mark': 2}
Is the ship movement cost supposed to be "10% of halite available at turn origin cell" as documented at Environment Rules or is it supposed to be 10% of halite stored on the ship as currently implemented.
In the first instance the cost is determined by the amount of halite on the map (it makes it more costly to move in areas high in halite). In the second case the cost is determined by the amount of halite on the ship (it always costs nearly half of your ship's halite to move 6 spaces).
(Question originally posed by Max333 at https://www.kaggle.com/c/halite/discussion/142301#804793)
KoreJson.getDoubleArrFromJson does not correctly parse arrays of doubles! This leads to kore being incorrect (zero) in Board.
Proposed fix:
for (int i = 0; i < arrStrParts.length; i++) {
Hi there
I decided today that I want to start doing Kaggle competitions for real. While browsing the site, I found out about simulation competitions which seem very exciting despite me only having basic knowledge on reinforcement learning.
Luckily this specific event is over, otherwise there might be some problems. If this package is used to test user submissions, then there may have been a security vulnerability with this game and users could have cheated to win every game they played against another competitor.
While stumbling across the rock paper scissors game, I tried to see if there was a way to use variables from outside the agent
function that is supplied by the user. I couldn't find one but it might still be possible with things like exec
. However, I found a way to return a variable that passes all the validation tests and lets the agent win.
The return variable from the user function is validated through this:
kaggle-environments/kaggle_environments/envs/rps/rps.py
Lines 15 to 20 in a5d9f75
Any subclass of int
could pass this.
The score is then evaluated here:
kaggle-environments/kaggle_environments/envs/rps/utils.py
Lines 4 to 11 in a5d9f75
If the return value is 1 then the left agent wins while if it is -1, the right agent wins.
Using this, we can make an agent that returns a subclass of int
but always makes itself win.
from kaggle_environments import evaluate, make, utils
def random_agent(observation, configuration):
import random
return random.choice(range(3))
def malicious_agent(observation, configuration):
class Fake(int):
def __sub__(self, other):
if (self + other) % 2 == 0:
# it is calculating right_move - left_move and we are the right_move
# we want the value to be as small as possible so we win
return -1
else:
# it is calculating left_move - right_move and we are the left_move
# we want the value to be as large as possible so we win
return 1
def __rsub__(self, other):
if (self + other) % 2 == 0:
return 1
else:
return -1
return Fake(1)
env = make("rps", debug=True)
env.reset()
env.run([random_agent, malicious_agent]) # also works if the arguments are [malicious_agent, random_agent]
env.render(mode="ipython")
I think this can be fixed by changing the isinstance(player.action, int)
to type(player.action) == int
in the following:
kaggle-environments/kaggle_environments/envs/rps/rps.py
Lines 16 to 20 in a5d9f75
This will force the agent function to return an int
and not anything that the user could have made themselves.
If I create multiple environments they will share variables (logs, steps, info). I believe this is not intended, or is it?
>>> env_a = kaggle_environments.make("lux_ai_2021", configuration=configuration, debug=True)
>>> env_b = kaggle_environments.make("lux_ai_2021", configuration=configuration, debug=True)
>>> env_a.logs is env_b.logs
True
If this is not intended it is likely caused by the following code:
In the following code python variables are initiated with empty lists and dictionaries. But this happens during function definition time and not during the running of the function.
def make(environment, configuration={}, info={}, steps=[], logs=[], debug=False, state=None):
See the discussion at stackoverflow or the warning in the python docs for details.
def make(environment, configuration=None, info=None, steps=None, logs=None, debug=False, state=None):
if configuration is None:
configuration = {}
if info is None:
info = {}
if steps is None:
steps = []
if logs is None:
logs = []
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.