Git Product home page Git Product logo

kaggle-environments's People

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

kaggle-environments's Issues

HTML replays get heavier and heavier

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.

Boards smaller than 21x21 can have more than 500 halite at one spot

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], ...}

GFootball: unable to load steps on environment

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?

ModuleNotFoundError: No module named 'kaggle_environments.envs.hungry_geese'

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'

ConnectX: UI bug when inarow != 4

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.

Package dependencies are not in setup.py install_requires

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.

Environment.render() does nothing with default arguments

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.

Halite env helpers.py wrong calculation in "to_index" and "from_index"

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)

Kore-2022 Java interpreter errors when wrapping

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]())

Existing agents broken by recent commit

Hi,

Recently via #134 (commit 33b6561) a new configuration variable was added: __raw_path__

configuration["__raw_path__"] = raw

This entry seems to be retained when the configuration is passed to my agents.

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!

increase max_log_length from 1024 to 2048 or 4096?

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.

problem about the python Memory leak?

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.

Halite reset method does not randomize the board any more

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?

Manual play not working

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

Halite: Add events to environment

Problem

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:

  • The amount of halite mined by a ship.
  • The amount of halite burnt by moving a ship.
  • The amount of halite deposited by a ship into a shipyard.
  • The number of collisions with friendly/enemy units.

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.

Proposed solution

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.

Caveats

This solution come with some caveats:

  • It is still difficult to distinguish between collisions with friendly units, enemy ships or enemy shipyards
    • Possible solution: Add a data property to the event that can contain arbitrary JSON (event specific information)

Next Steps

If you like this idea, I'd be happy to be involved in the definition of the events and the development. :)

Issue with installing Kaggle-environments on Ubuntu 18.04

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,

Halite: Units are ingored for collision detection after agent sends non-executable action

  1. Agents can change to an inactive status (meaning 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."
  1. Inactive agents are ignored when calculating the collision board (source):
board = [[-1, {}] for _ in range(size ** 2)]
for index, agent in enumerate(state):
    if agent.status != "ACTIVE":
        continue
    # ...
  1. The collision board is then used to calculate collisions and damage/destroy ships.

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.

Abstract solution

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? ๐Ÿค”

More general problem

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. :)

Suggestion: Add custom labels to to issues and allow users to tag them

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?

Halite: Unexpected values reported in obs.step

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

Why don't you tag the releases anymore?

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.

Bug for version 1.7.3: 'str' object has no attribute 'action'

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.

StdErr being truncated, can we increase maximum size?

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?

Halite: Evaluating a SPAWN or CONVERT action makes the interpreter break out of the apply actions loop

Issue

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).

Reason

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.

Proposed Solution

  1. Understand the underlying game logic. Is there a reason for those 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.
  2. If this actually turns out to be a bug, I assume removing the break statements should be sufficient. I'd be happy to submit a PR against that. :)

Can't install git version with poetry

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.

Halite: spawning ships overrides ships_by_uid dict

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

Use kaggle-environments without gfootball

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?

Halite 0.3.2: Idle agents win all collisions

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.

Hungry Geese: When "ERROR", the goose is frozen without disappearing

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).

if agent.status != "ACTIVE":
continue


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.

"enum": ["INACTIVE", "ACTIVE", "DONE", "ERROR", "INVALID", "TIMEOUT"]

if isinstance(action, DeadlineExceeded):
self.debug_print(f"Timeout: {str(action)}")
action_state[index]["status"] = "TIMEOUT"
elif isinstance(action, BaseException):
self.debug_print(f"Error: {traceback.format_exception(None, action, action.__traceback__)}")
action_state[index]["status"] = "ERROR"
else:
err, data = process_schema(
self.__state_schema.properties.action, action)
if err:
self.debug_print(f"Invalid Action: {str(err)}")
action_state[index]["status"] = "INVALID"
else:
action_state[index]["action"] = data
self.state = self.__run_interpreter(action_state, logs)
# Max Steps reached. Mark ACTIVE/INACTIVE agents as DONE.
if self.state[0].observation.step >= self.configuration.episodeSteps - 1:
for s in self.state:
if s.status == "ACTIVE" or s.status == "INACTIVE":
s.status = "DONE"

Halite: Halite amount can grow negative

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.

Proposed solution

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.

How to get this to work on PyCharm?

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

Halite 0.3.2: Halite can STILL be negative

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]

Halite: Point comparison fails when comparing to non-subscriptable object

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

Threading not working

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`

All: Incomplete observation when second player

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}

Current Discussion
Original Discussion

Halite: Ship movement costs?

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)

Lack of input validation for Rock, Paper, Scissors simulation

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:

def is_valid_action(player, sign_count):
return (
player.action is not None and
isinstance(player.action, int) and
0 <= player.action < sign_count
)

Any subclass of int could pass this.

The score is then evaluated here:

def get_score(left_move, right_move):
# This method exists in this file so it can be consumed from rps.py and agents.py without a circular dependency
delta = (
right_move - left_move
if (left_move + right_move) % 2 == 0
else left_move - right_move
)
return 0 if delta == 0 else math.copysign(1, delta)

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:

return (
player.action is not None and
isinstance(player.action, int) and
0 <= player.action < sign_count
)

This will force the agent function to return an int and not anything that the user could have made themselves.

Environments share information

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 = []

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.