lux-ai-challenge / lux-design-s1 Goto Github PK
View Code? Open in Web Editor NEWHome to the design and engine of the @Lux-AI-Challenge Season 1, hosted on @kaggle
Home Page: https://lux-ai.org/
License: Apache License 2.0
Home to the design and engine of the @Lux-AI-Challenge Season 1, hosted on @kaggle
Home Page: https://lux-ai.org/
License: Apache License 2.0
Currently most kits have the first turn indexed at turn 1. visualizer starts at turn 0.
Also means the night time check should be fixed as well.
There is a one off error either in the engine, kit, or visualizer.
E.g. visualizer shows the first turn as turn 0 and actually renders the end of the turn 0.
Kits are 1-indexed (due to engine starting at turn 1).
While using namespace std;
may have some benefits, it has some downsides too. Therefore the decision whether or not to use the namespace should be left to the person writing the bot. However, when you put using namespace std;
in a header at global scope, that means that you make that decision for the user of the library, as they can't remove this statement.
The fix for this would be to move the using namespace std;
into the kit
namespace, so this
using namespace std;
namespace kit {
// ...
}
would become this
namespace kit {
using namespace std;
// ...
}
This would be a breaking change though, but given that it's quite early in the competition, I think this is acceptable.
This is especially for users who want to submit C++ compiled binaries to the competition. This is allowed by Kaggle but to make it work you will want to compile inside a docker container.
TypeError: Cannot read property 'hasResource' of undefined
at Object.generateGame (/opt/homebrew/lib/node_modules/@lux-ai/2021-challenge/lib/es5/Game/gen.js:151:37)
at Function.<anonymous> (/opt/homebrew/lib/node_modules/@lux-ai/2021-challenge/lib/es5/logic.js:87:38)
at step (/opt/homebrew/lib/node_modules/@lux-ai/2021-challenge/lib/es5/logic.js:33:23)
at Object.next (/opt/homebrew/lib/node_modules/@lux-ai/2021-challenge/lib/es5/logic.js:14:53)
at /opt/homebrew/lib/node_modules/@lux-ai/2021-challenge/lib/es5/logic.js:8:71
at new Promise (<anonymous>)
at __awaiter (/opt/homebrew/lib/node_modules/@lux-ai/2021-challenge/lib/es5/logic.js:4:12)
at Function.LuxDesignLogic.initialize (/opt/homebrew/lib/node_modules/@lux-ai/2021-challenge/lib/es5/logic.js:60:16)
at LuxDesign.<anonymous> (/opt/homebrew/lib/node_modules/@lux-ai/2021-challenge/lib/es5/design.js:67:62)
at step (/opt/homebrew/lib/node_modules/@lux-ai/2021-challenge/lib/es5/design.js:48:23)
The validateCommand() function checks if the [x, y] coordinates for being ints but not being in the map.
and update docs
[ ] Story!
[ ] Technical
Currently, Position.direction_to(target_pos: Position) -> DIRECTIONS
will return only a single direction. However, most of the time there are two possible directions for the shortest path. For implementing a move optimizer it is important to know all possible shortest path directions as there are usually 2 (unless some coordinate is identical).
Therefore, I suggest to implement the function as Position.direction_to(target_pos: Position) -> Set[DIRECTION]
which returns a set of all directions in order to most quickly reach the destination.
For example Position(1,1).direction_to(Position(2,2)) -> {"s", "e"}
It looks like unit actions are handled sequentially in an arbitrary order. This might cause some confusing behaviour where actions that might seem valid/reasonable will silently fail.
Eg if I had:
worker_1: 80 wood, 20 coal
worker_2: 20 wood
and I wanted to do two transfers so that worker_1 can go build a city:
worker_1 --> worker_2: 20 coal
worker_2 --> worker_1: 20 wood
this may or may not work, depending on the order of execution. If worker_2 did its transfer first, it would silently fail because worker_1 is at capacity.
At the moment we do not provide a nice way for users to provide state + actions and receive the next state
A few issues:
This issue would mean a somewhat considerable overhaul
One option:
Another option with less work
We added instructions for people with the tar
command, need instructions for windows users.
the cli tool doesn't let you ctrl-c in the middle.
the cli tool should 1. let you ctrl-c and 2. auto close after a match runs.
Parameter changes
Considering some penalties e.g. city expansion penalties
I think there are big memory leaks in cpp kit
here
void resetPlayerStates ()
{
...
players[0].cities.clear ();
...
}
cities is std::map<std::string, City *>
each turn we make new allocation
this->players[team].cities[cityid] = new lux::City (team, cityid, fuel, lightUpkeep);
but we never do smth like
delete players[team].cities[cityid];
(clear () doesn't do it for dynamic memory)
the same problem with std::vector<CityTile *> citytiles;
because of this, sometimes an error occurs in long games
RuntimeError: abort(RuntimeError: abort(Cannot enlarge memory arrays to size 16781312 bytes (OOM). Either (1) compile with -s INITIAL_MEMORY=X with X higher than the current value 16777216, (2) compile with -s ALLOW_MEMORY_GROWTH=1 which allows increasing the size at runtime, or (3) if you want malloc to return NULL (0) instead of this abort, compile with -s ABORTING_MALLOC=0 ) at Error
I removed these pointers locally and this solved the problem.
...
Check it, please
add CLI option to run matches that then create stateful replays or convert action based replays for e.g. easier analysis
Users have to run tar -czvf out.tar.gz * within the bot folder for submissions to kaggle work.
Might also want to provide scripts instead (that resolve path stuff)
Would be nice to have some tutorial docs written up before the official release on Kaggle that take someone step by step on how to make a simple bot that can keep a single city alive, then keep several cities alive and build more units, then suggest some slightly advanced paths one could take
Open to the community to helping build one of these! (just don't include a tutorial on a too advanced of a bot)
Some users see the collision warnings as being too verbose
One option here is instead of setting the suppress argument to true, set it to a list of warnings to suppress
There is an inMap check missing from the ACTIONS.RESEARCH section of validateCommand().
Kaggle environments will need this as they need the updated state (and use the updates sent to agents to update the state) to get the correct rewards and decide win/lose.
Map Generation:
Thoughts on revamping it to some extent? e.g. encourage more "forests" and whatever.
Cooldown always goes down by 1 each turn
Cooldown increases by a base amount depending on unit. Increases less if tile unit is currently on is developed (has roads).
Consider giving workers a base cooldown of 2.
I don't know how to explain this. I've literally debugged this for days, even rewrote my multi-file project into a one-file one, and I am still not sure what causes it. I am on Linux, and program in the C++ v1.1.x kit.
Basically, there are certain seeds that cause my bot to crash in one gigantic memory overflow, which is always near turn 340-360. The error always looks like this, though memory access out of bounds
can also happen:
[Agent 0 Log] Cannot enlarge memory arrays to size 16781312 bytes (OOM). Either (1) compile with -s INITIAL_MEMORY=X with X higher than the current value 16777216, (2) compile with -s ALLOW_MEMORY_GROWTH=1 which allows increasing the size at runtime, or (3) if you want malloc to return NULL (0) instead of this abort, compile with -s ABORTING_MALLOC=0
[Agent 0 Log] exception thrown: RuntimeError: abort(Cannot enlarge memory arrays to size 16781312 bytes (OOM). Either (1) compile with -s INITIAL_MEMORY=X with X higher than the current value 16777216, (2) compile with -s ALLOW_MEMORY_GROWTH=1 which allows increasing the size at runtime, or (3) if you want malloc to return NULL (0) instead of this abort, compile with -s ABORTING_MALLOC=0 ) at Error
[Agent 0 Log] at jsStackTrace (/home/diego/Desktop/LuxBot/main.js:1807:19)
[Agent 0 Log] at stackTrace (/home/diego/Desktop/LuxBot/main.js:1824:16)
[Agent 0 Log] at abort (/home/diego/Desktop/LuxBot/main.js:1543:44)
[Agent 0 Log] at abortOnCannotGrowMemory (/home/diego/Desktop/LuxBot/main.js:1931:7)
[Agent 0 Log] at _emscripten_resize_heap (/home/diego/Desktop/LuxBot/main.js:1936:7)
[Agent 0 Log] at <anonymous>:wasm-function[3226]:0x60e6e
[Agent 0 Log] at malloc (<anonymous>:wasm-function[3220]:0x5edc8)
[Agent 0 Log] at <anonymous>:wasm-function[3002]:0x5acdc
[Agent 0 Log] at <anonymous>:wasm-function[102]:0x6b50
[Agent 0 Log] at <anonymous>:wasm-function[57]:0x41ba,RuntimeError: abort(Cannot enlarge memory arrays to size 16781312 bytes (OOM). Either (1) compile with -s INITIAL_MEMORY=X with X higher than the current value 16777216, (2) compile with -s ALLOW_MEMORY_GROWTH=1 which allows increasing the size at runtime, or (3) if you want malloc to return NULL (0) instead of this abort, compile with -s ABORTING_MALLOC=0 ) at Error
Keep in mind that this only happens rarely, like 1 in 50 seeds.
Here is a one file version of my bot using the cpp kit in the v1.1.x branch:
#include "lux/kit.hpp"
#include "lux/define.cpp"
#include <string.h>
#include <vector>
#include <set>
#include <stdio.h>
#include <map>
using namespace std;
using namespace lux;
int main()
{
kit::Agent gameState;
gameState.initialize();
while (true)
{
GameMap& gameMap = gameState.map;
Player& player = gameState.players[gameState.id];
vector<string> actions;
gameState.update();
vector<Cell*> resourceTiles;
for (int y = 0; y < gameMap.height; y++)
{
for (int x = 0; x < gameMap.width; x++)
{
Cell *cell = gameMap.getCell(x, y);
if (cell->hasResource())
{
resourceTiles.push_back(cell);
}
}
}
for (int i = 0; i < player.units.size(); i++)
{
Unit& unit = player.units[i];
if(unit.isWorker() && unit.canAct()) {
if(unit.getCargoSpaceLeft() != 0) {
ResourceType resourceToFind = ResourceType::wood;
if(player.researchedCoal()) resourceToFind = ResourceType::coal;
if(player.researchedUranium()) resourceToFind = ResourceType::uranium;
Cell* closestResourceTile = nullptr;
float closestDist = 999999;
for(auto it = resourceTiles.begin(); it != resourceTiles.end(); it++) {
Cell* cell = *it;
float dist = cell->pos.distanceTo(unit.pos);
if(cell->resource.type == resourceToFind && dist < closestDist) {
closestDist = dist;
closestResourceTile = cell;
}
}
DIRECTIONS dir = unit.pos.directionTo(closestResourceTile->pos);
actions.push_back(unit.move(dir));
}
else {
CityTile* closestCityTile = nullptr;
float closestDist = 999999;
for(auto city : player.cities) {
for(auto cityTile : city.second->citytiles) {
float dist = cityTile->pos.distanceTo(unit.pos);
if(dist < closestDist) {
closestCityTile = cityTile; closestDist = dist;
}
}
}
DIRECTIONS dir = unit.pos.directionTo(closestCityTile->pos);
actions.push_back(unit.move(dir));
}
}
}
for (int i = 0; i < actions.size(); i++)
{
if (i != 0)
cout << ",";
cout << actions[i];
}
cout << endl;
// end turn
gameState.end_turn();
}
return 0;
}
To get the same error, you must crosscompile main.cpp into a JS file then run the main.js
it against itself in seed 556459304
using the v1.1.x cpp kit.
Here are noteworthy findings:
Allow it such that you can build a city tile with 100 of any resource.
This makes programming easier, and is a softer limit and people can choose to write code to mine wood only for efficient building and less waste if they want
should use max(this.width, this.height) to sort by a unique number for each tile.
633636160 has this
The call signature of the js transfer
method here doesn't match the docs.
Also, I suppose it could be simplified down to something like this:
transfer(destUnit, resourceType, amount) {
let destID = typeof destUnit === "string" ? destUnit : destUnit.id;
return `t ${this.id} ${destID} ${resourceType} ${amount}`;
}
I've not actually tested this.
So I've heard, you can run cpp files directly into lux-ai-2021. However, when I try to run it with my own bot, I get this error. https://pastebin.com/CQDev0Kw error in its entirety.
diego@adhoc:~/Desktop/LuxBot$ sudo lux-ai-2021 src/main.cpp src/main.cpp
-=-=-=-=-=-=-=-=-=-=-=-| [INFO] match_QiUDBKVEPU7A |-=-=-=-=-=-=-=-=-=-=-=-
[INFO] (match_QiUDBKVEPU7A) - Design: lux_ai_2021 | Initializing match - ID: QiUDBKVEPU7A, Name: match_QiUDBKVEPU7A
Error: spawn ETXTBSY
at ChildProcess.spawn (internal/child_process.js:403:11)
at spawn (child_process.js:580:9)
at Object.spawnWithSignal [as spawn] (child_process.js:717:17)
at Object.spawn [as default] (/usr/local/lib/node_modules/@lux-ai/2021-challenge/node_modules/cross-spawn/index.js:12:24)
at /usr/local/lib/node_modules/@lux-ai/2021-challenge/node_modules/dimensions-ai/lib/main/Agent/index.js:653:46
at new Promise (<anonymous>)
at Agent._spawnProcess (/usr/local/lib/node_modules/@lux-ai/2021-challenge/node_modules/dimensions-ai/lib/main/Agent/index.js:646:16)
at Agent.<anonymous> (/usr/local/lib/node_modules/@lux-ai/2021-challenge/node_modules/dimensions-ai/lib/main/Agent/index.js:628:56)
at step (/usr/local/lib/node_modules/@lux-ai/2021-challenge/node_modules/dimensions-ai/lib/main/Agent/index.js:46:23)
at Object.next (/usr/local/lib/node_modules/@lux-ai/2021-challenge/node_modules/dimensions-ai/lib/main/Agent/index.js:27:53) {
errno: -26,
code: 'ETXTBSY',
syscall: 'spawn'
}
I get this error when trying to run the v1.1.x branch of the cpp kit as well; Not sure what is causing it. It works fine when transpiled into JS. I've tried running the lux-ai-2021
command as sudo
and not sudo
, though it doesn't make a difference. Here's the exact main.cpp
code I tried to run in simple/:
#include "lux/kit.hpp"
#include "lux/define.cpp"
#include <string.h>
#include <vector>
#include <set>
#include <stdio.h>
using namespace std;
using namespace lux;
int main()
{
kit::Agent gameState = kit::Agent();
gameState.initialize();
while (true)
{
gameState.update();
vector<string> actions = vector<string>();
Player player = gameState.players[gameState.id];
Player opponent = gameState.players[(gameState.id + 1) % 2];
GameMap gameMap = gameState.map;
vector<Cell *> resourceTiles = vector<Cell *>();
for (int y = 0; y < gameMap.height; y++)
{
for (int x = 0; x < gameMap.width; x++)
{
Cell *cell = gameMap.getCell(x, y);
if (cell->hasResource())
{
resourceTiles.push_back(cell);
}
}
}
int citiesToBuild = 0;
for (auto it : player.cities)
{
City *city = it.second;
if (city->fuel > city->getLightUpkeep() * (int) GAME_CONSTANTS["PARAMETERS"]["NIGHT_LENGTH"] + 1000)
{
citiesToBuild += 1;
}
for (auto citytile : city->citytiles)
{
if (citytile->canAct()) {
// you can use the following to get the citytile to research or build a worker
// actions.push_back(citytile.research());
// actions.push_back(citytile.buildWorker());
}
}
}
for (int i = 0; i < player.units.size(); i++)
{
Unit unit = player.units[i];
if (unit.isWorker() && unit.canAct())
{
if (unit.getCargoSpaceLeft() > 0)
{
// if the unit is a worker and we have space in cargo, lets find the nearest resource tile and try to mine it
Cell *closestResourceTile;
float closestDist = 9999999;
for (auto it = resourceTiles.begin(); it != resourceTiles.end(); it++)
{
auto cell = *it;
if (cell->resource.type == ResourceType::coal && !player.researchedCoal()) continue;
if (cell->resource.type == ResourceType::uranium && !player.researchedUranium()) continue;
float dist = cell->pos.distanceTo(unit.pos);
if (dist < closestDist)
{
closestDist = dist;
closestResourceTile = cell;
}
}
if (closestResourceTile != nullptr)
{
auto dir = unit.pos.directionTo(closestResourceTile->pos);
actions.push_back(unit.move(dir));
}
}
else
{
if (player.cities.size() > 0)
{
auto city_iter = player.cities.begin();
auto city = city_iter->second;
float closestDist = 999999;
CityTile *closestCityTile;
for (auto citytile : city->citytiles)
{
float dist = citytile->pos.distanceTo(unit.pos);
if (dist < closestDist)
{
closestCityTile = citytile;
closestDist = dist;
}
}
if (closestCityTile != nullptr)
{
auto dir = unit.pos.directionTo(closestCityTile->pos);
if (citiesToBuild > 0 && unit.pos.isAdjacent(closestCityTile->pos) && unit.canBuild(gameMap))
{
actions.push_back(unit.buildCity());
}
else
{
actions.push_back(unit.move(dir));
}
}
}
}
}
}
for (int i = 0; i < actions.size(); i++)
{
if (i != 0)
cout << ",";
cout << actions[i];
}
cout << endl;
gameState.end_turn();
}
return 0;
}
Sometimes it's lowercase and sometimes it's all upper case
A suggestion would be to use Python Enum for constants, because they provide some small advantages over plain class constants. For example
from enum import Enum
class DIRECTION(Enum):
NORTH = "n"
WEST = "w"
SOUTH = "s"
EAST = "e"
CENTER = "c"
While they can be used similarly as in the previous version, small advantages - apart from being more pythonic - are
>>> list(DIRECTION) # possible to iterate; very useful sometimes
[<DIRECTION.NORTH: 'n'>,
<DIRECTION.WEST: 'w'>,
<DIRECTION.SOUTH: 's'>,
<DIRECTION.EAST: 'e'>,
<DIRECTION.CENTER: 'c'>]
>>> DIRECTION.NORTH.name # access to readable name for print output
'NORTH'
>>> DIRECTION.NORTH.value # access to raw string value, but there shouldn't be a reason, unless you process external output
'n'
Other advantages are that type-checkers like mypy will notice if you accidentally compare them against unrelated strings.
However, note that if for whatever reason you introduce raw strings instead of using the proper class, you will need to access the value with DIRECTION.NORTH.value
(for example for if my_direction.value == "n":
)
I would like to use the default viewer https://2021vis.lux-ai.org/
to view the sample_replay.json
But I get this message:
When I try to manually build the previous version of the viewer I get the messages mentioned in this issue Install/setup dev questions
Here is another example:
root@docker-ubuntu-s-1vcpu-2gb-nyc1-01:~/ap/viewer-ver1/LuxViewer2021-1.0.0# npm i -g serve
/root/ap/emsdk/node/14.15.5_64bit/bin/serve -> /root/ap/emsdk/node/14.15.5_64bit/lib/node_modules/serve/bin/serve.js
+ serve@12.0.0
updated 1 package in 4.665s
root@docker-ubuntu-s-1vcpu-2gb-nyc1-01:~/ap/viewer-ver1/LuxViewer2021-1.0.0# serve dist
ERROR: Cannot copy to clipboard: Both xsel and fallback failed
┌───────────────────────────────────┐
│ │
│ Serving! │
│ │
│ Local: http://localhost:5000 │
│ │
└───────────────────────────────────┘
^C
INFO: Gracefully shutting down. Please wait...
root@docker-ubuntu-s-1vcpu-2gb-nyc1-01:~/ap/viewer-ver1/LuxViewer2021-1.0.0# cp -r assets/ dist
root@docker-ubuntu-s-1vcpu-2gb-nyc1-01:~/ap/viewer-ver1/LuxViewer2021-1.0.0# serve dist
root@docker-ubuntu-s-1vcpu-2gb-nyc1-01:~/ap/viewer-ver1/LuxViewer2021-1.0.0# npm run dev
> lux-viewer-2021@1.0.0 dev /root/ap/viewer-ver1/LuxViewer2021-1.0.0
> webpack --mode development && webpack-dev-server --mode development --devtool source-map
sh: 1: webpack: not found
npm ERR! code ELIFECYCLE
npm ERR! syscall spawn
npm ERR! file sh
npm ERR! errno ENOENT
npm ERR! lux-viewer-2021@1.0.0 dev: `webpack --mode development && webpack-dev-server --mode development --devtool source-map`
npm ERR! spawn ENOENT
npm ERR!
npm ERR! Failed at the lux-viewer-2021@1.0.0 dev script.
npm ERR! This is probably not a problem with npm. There is likely additional logging output above.
npm WARN Local package.json exists, but node_modules missing, did you mean to install?
npm ERR! A complete log of this run can be found in:
npm ERR! /root/.npm/_logs/2021-07-05T21_41_41_040Z-debug.log
Let user annotate onto the game with text. There are two places. Putting text into the map, syntax then could be
text(x, y, message, fontsize)
There could also be text on the side, syntax could be
sidetext(message)
This would be on the side in a two column thing
Thanks to a member from the community!
Lux_AI_Challance_-_Improvements.pdf
Sticking this here just to keep it alive and tracked on github.
The main thing I think that actually should be done is regrowth for wood given that wood is used for city building.
The other thing is either making research passive (since you always want to research if you aren't building units) or making research have a higher opportunity cost in relation to the number of units you can build.
As most recently mentioned by snozzled, the game state is generated with cell.road instead of cell.getRoad(), so the hardcoded exception for CityTiles is skipped. This is not an issue with the gameplay since the engine still uses cell.getRoad().
Bug: Multiple redefinition of internal lux/
files when importing a header / cpp file combo that both contain lux/kit.hpp
.
Minimum possible example from simple/
in kits/cpp
main.cpp
#include "lux/kit.hpp"
#include "foo.h"
#include <string.h>
#include <vector>
#include <set>
#include <stdio.h>
using namespace std;
using namespace lux;
int main()
...
foo.h
#ifndef FOO_H
#define FOO_H
#include "lux/kit.hpp"
void foo();
#endif
foo.cpp
#include "lux/kit.hpp"
#include "foo.h"
void foo() {
int i = 1 + 1;
}
Compile command: emcc -s FORCE_FILESYSTEM=1 --pre-js internals/init_fs.js main.cpp foo.cpp -o main.js
Error: https://pastebin.com/5G0Garja
Note: Manually adding header guards to each file in lux , specifically kit.hpp, alleviated most errors. This change has a pull request already. The remaining errors after this change are here: https://pastebin.com/YMJXAFDC
Note 2: This would've thrown an error anyway if you didn't mess with main.js. In fact, this is inherently a C++ problem because if you run g++ main.cpp foo.cpp
you get the same error
Possible solution: It's possible to simply move all static variable definitions into a .cpp file in /lux, then put that .cpp file in the compile command and it works just fine as there is now only one definition of those static variables
As I originally tried to make sense of the validateCommand() function, I noticed a couple inconsistencies, which at the time I did not know were bugs or not. Separating the error check from the throwing of the warning in that function would improve readability greatly and would make making changes much easier.
we say in our specs its uranium tiles first, then coal tiles, then wood tiles, but this is not the case...
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.