Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 49 additions & 0 deletions kits/julia/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
# Julia Kit

This is the folder for the Julia kit. Please make sure to read the instructions as they are important regarding how you will write a bot and submit it to the competition servers.

Make sure to check our [Discord](https://discord.gg/aWJt3UAcgn) or the [Kaggle forums](https://www.kaggle.com/c/lux-ai-2021/discussion) for announcements if there are any breaking changes.

## Getting Started

To get started, download the `simple` folder from this repository.

Then navigate to that folder via command line e.g. `cd simple` or for windows `chdir simple`.

Your main code will go into `bot/src/main/julia/XXX.jl` and you can use create other files to help you as well. You should leave `main.py` and the `lux` package alone in `bot/src/main/julia/`. Read the `Bot.jl` file to get an idea of how a bot is programmed and a feel for the Julia API.

Make sure you have Julia v1.6 (something about competition server).

To confirm your setup, in your bot folder run

To then test run your bot, run

```
lux-ai-2021 run.sh run.sh --out=replay.json
```

which should produce no errors.

To debug your bot locally start a game with `debug.sh` script (also increase the bot timeout):

```lux-ai-2021 run.sh debug.sh --maxtime=9999999```

Then, connect the debugger to `localhost:5005`. Now you can use breakpoints and other debug features!

If you find some bugs or unfixable errors, please let an admin know via Discord, the forums, or email us.

## Developing

Now that you have some code and you checked that your code works by trying to submit something, you are now ready to start programming your bot and having fun!

If you haven't read it already, take a look at the [design specifications for the competition](https://lux-ai.org/specs-2021). This will go through the rules and objectives of the competition.

All of our kits follow a common API through which you can use to access various functions and properties that will help you develop your strategy and bot. The markdown version is here: https://github.com/Lux-AI-Challenge/Lux-Design-2021/blob/master/kits/README.md

## Submitting to Kaggle

You can just create submission archive using `./pack.sh`. Upload `submission.tar.gz` to Kaggle "Submission" section.

## FAQ

As questions come up, this will be populated with frequently asked questions regarding the Julia kit.
9 changes: 9 additions & 0 deletions kits/julia/simple/lux/Project.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
name = "LuxAI"
uuid = "2dcf1b2f-c367-41fa-9531-ffe19c618c81"
authors = ["José Bayoán Santiago Calderón <naoyabpr@gmail.com>"]
version = "0.1.0"

[deps]
JSON3 = "0f8b85d8-7281-11e9-16c2-39a750bddbf1"
PackageCompiler = "9b87118b-4619-50d2-8e1e-99f35a4d4d9d"
PyCall = "438e738f-606a-5dbb-bf0a-cddfbfd45ab0"
14 changes: 14 additions & 0 deletions kits/julia/simple/lux/src/LuxAI.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
module LuxAI

using JSON3: JSON3, StructType, Struct

for file in ["annotate", "constants", "game_objects", "game"]
include(joinpath("lux", "$file.jl"))
end

# function julia_main()::Cint
# # do something based on ARGS?
# return 0 # if things finished successfully
# end

end
37 changes: 37 additions & 0 deletions kits/julia/simple/lux/src/annotate.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
"""
circle(x::Integer, y::Integer) :: String

Returns the draw circle annotation action.
Will draw a unit sized circle on the visualizer at the current turn centered at the `Cell` at the given x, y coordinates.
"""
circle(x::Integer, y::Integer) = "dc $x $y"
"""
x(x::Integer, y::Integer) :: String

Returns the draw X annotation action.
Will draw a unit sized X on the visualizer at the current turn centered at the `Cell` at the given x, y coordinates.
"""
x(x::Integer, y::Integer) = "dx $x $y"
"""
line(x1::Integer, y1::Integer, x2::Integer, y2::Integer) :: String

Returns the draw line annotation action.
Will draw a line from the center of the `Cell` at (x1, y1) to the center of the `Cell` at (x2, y2).
"""
line(x1::Integer, y1::Integer, x2::Integer, y2::Integer) = "dl $x1 $y1 $x2 $y2"
"""
text(x::Integer, y::Integer, message::AbstractString,
fontsize::Integer = 16) :: String

Returns the draw text annotation action.
Will write text on top of the tile at (x, y) with the particular message and fontsize.
"""
text(x::Integer, y::Integer, message::AbstractString, fontsize::Integer = 16) =
"dt $x $y '$message' $fontsize"
"""
sidetext(message::AbstractString) :: String

Returns the draw side text annotation action.
Will write text that is displayed on that turn on the side of the visualizer.
"""
sidetext(message::AbstractString) = "dst '$message'"
15 changes: 15 additions & 0 deletions kits/julia/simple/lux/src/constants.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
"""
GameConstants(jsonfile::AbstractString = joinpath(pkgdir(LuxAI), "src", "lux", "game_constants.json"))

This will contain constants on all game parameters like the max turns, the light upkeep of CityTiles etc.
If there are any crucial changes to the starter kits, typically only this object will change.
"""
struct GameConstants
UNIT_TYPES :: NamedTuple{(:WORKER, :CART), NTuple{2, Int8}}
RESOURCE_TYPES :: NamedTuple{(:WOOD, :COAL, :URANIUM), NTuple{3, String}}
INPUTS :: NamedTuple{(:RESEARCH_POINTS, :RESOURCES, :UNITS, :CITY, :CITY_TILES, :ROADS, :DONE), NTuple{7, String}}
DIRECTIONS :: NamedTuple{(:NORTH, :WEST, :EAST, :SOUTH, :CENTER), NTuple{5, String}}
PARAMETERS :: NamedTuple{(:DAY_LENGTH, :NIGHT_LENGTH, :MAX_DAYS, :LIGHT_UPKEEP, :WOOD_GROWTH_RATE, :MAX_WOOD_AMOUNT, :CITY_BUILD_COST, :CITY_ADJACENCY_BONUS, :RESOURCE_CAPACITY, :WORKER_COLLECTION_RATE, :RESOURCE_TO_FUEL_RATE, :RESEARCH_REQUIREMENTS, :CITY_ACTION_COOLDOWN, :UNIT_ACTION_COOLDOWN, :MAX_ROAD, :MIN_ROAD, :CART_ROAD_DEVELOPMENT_RATE, :PILLAGE_RATE), Tuple{Int, Int, Int, NamedTuple{(:CITY, :WORKER, :CART), NTuple{3, Int}}, Float64, Float64, Int, Int, NamedTuple{(:WORKER, :CART), NTuple{2, Int}}, NamedTuple{(:WOOD, :COAL, :URANIUM), NTuple{3, Int}}, NamedTuple{(:WOOD, :COAL, :URANIUM), NTuple{3, Int}}, NamedTuple{(:COAL, :URANIUM), NTuple{2, Int}}, Int, NamedTuple{(:WORKER, :CART), NTuple{2, Int}}, Int, Int, Float64, Float64}}
end

StructType(::Type{GameConstants}) = Struct()
118 changes: 118 additions & 0 deletions kits/julia/simple/lux/src/game.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
"""
Game(messages::AbstractDict,
configuration::GameConstants = JSON3.read(read(joinpath(pkgdir(LuxAI), "src", "lux", "game_constants.json"), String),
GameConstants))

Struct for the state of the game.
"""
struct Game
id :: Int
turn :: Ref{Int}
map :: GameMap
players :: NTuple{2, Player}
configuration :: GameConstants
function Game(observations::AbstractDict,
configuration::GameConstants = JSON3.read(read(joinpath(pkgdir(LuxAI), "src", "lux", "game_constants.json"), String), GameConstants))
updates = observations["updates"]
id = parse(Int, updates[1])
turn = Ref(observations["step"])
# get some other necessary initial input
game_map = GameMap(parse(Int, match.(r"\d+", updates[2]).match))
players = (Player(0), Player(1))
new(id, turn, game_map, players, configuration)
end
end
"""
_end_turn(::Game) :: Nothing

Prints the signal for end of turn.
"""
_end_turn(::Game) = print("D_FINISH")
"""
_reset_player_states!(obj::Game) :: Nothing

Resets the state of the players after finishing their turn.
"""
function _reset_player_states!(obj::Game)
for player in obj.players
empty!(player.units)
empty!(player.cities)
end
nothing
end
"""
_update!(obj::Game, messages)

Update state of the game.
"""
function _update!(obj::Game, observation::AbstractVector{<:AbstractString})
for col in axes(obj.map.map, 2)
for row in axes(obj.map.map, 1)
obj.map.map[row, col] = Cell(Position(row - 1, col - 1))
end
end
obj.turn.x += 1
_reset_player_states!(obj)
for update in observation
if update == "D_DONE"
break
end
observation = observation["updates"]
update = observation[end - 8]
strs = split(update, " ")
input_identifier = strs[1]
if input_identifier == obj.configuration.INPUTS.RESEARCH_POINTS
team = parse(Int, strs[2])
obj.players[team + 1].research_points = parse(Int, strs[3])
elseif input_identifier == obj.configuration.INPUTS.RESOURCES
type = strs[2]
x = parse(Int, strs[3])
y = parse(Int, strs[4])
amt = parse(Float64, strs[5])
pos = Position(x, y)
resource = Resource(type, amt)
obj.map.map[x + 1, y + 1] = Cell(pos, resource)
elseif input_identifier == obj.configuration.INPUTS.UNITS
unittype = parse(Int, strs[2])
unittype = findfirst(isequal(unittype), values(obj.configuration.UNIT_TYPES))
unittype = string(keys(obj.configuration.UNIT_TYPES)[unittype])
team = parse(Int, strs[3])
unitid = strs[4]
x = parse(Int, strs[5])
y = parse(Int, strs[6])
pos = Position(x, y)
cooldown = parse(Float64, strs[7])
wood = parse(Int, strs[8])
coal = parse(Int, strs[9])
uranium = parse(Int, strs[10])
cargo = Cargo(;wood, coal, uranium)
push!(obj.players[team + 1].units, Unit(unitid, team, pos, unittype, cooldown, cargo))
elseif input_identifier == obj.configuration.INPUTS.CITY
team = parse(Int, strs[2])
cityid = strs[3]
fuel = parse(Float64, strs[4])
lightupkeep = parse(Float64, strs[5])
obj.players[team + 1].cities[cityid] = City(cityid, team, fuel, lightupkeep)
elseif input_identifier == obj.configuration.INPUTS.CITY_TILES
team = parse(Int, strs[2])
cityid = strs[3]
x = parse(Int, strs[4])
y = parse(Int, strs[5])
pos = Position(x, y)
cooldown = parse(Float64, strs[6])
city = obj.players[team + 1].cities[cityid]
citytile = CityTile(cityid, team, pos, cooldown)
push!(city.citytiles, citytile)
cell = get_cell_by_pos(obj.map, pos)
cell.citytile = citytile
obj.players[team + 1].city_tile_count += 1
elseif input_identifier == obj.configuration.INPUTS.ROADS
x = parse(Int, strs[2])
y = parse(Int, strs[3])
pos = Position(x, y)
road = parse(Float64, strs[4])
cell = get_cell_by_pos(obj.map, pos)
cell.road = road
end
end
end
68 changes: 68 additions & 0 deletions kits/julia/simple/lux/src/game_constants.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
{
"UNIT_TYPES": {
"WORKER": 0,
"CART": 1
},
"RESOURCE_TYPES": {
"WOOD": "wood",
"COAL": "coal",
"URANIUM": "uranium"
},
"INPUTS": {
"RESEARCH_POINTS": "rp",
"RESOURCES": "r",
"UNITS": "u",
"CITY": "c",
"CITY_TILES": "ct",
"ROADS": "ccd",
"DONE": "D_DONE"
},
"DIRECTIONS": {
"NORTH": "n",
"WEST": "w",
"EAST": "e",
"SOUTH": "s",
"CENTER": "c"
},
"PARAMETERS": {
"DAY_LENGTH": 30,
"NIGHT_LENGTH": 10,
"MAX_DAYS": 360,
"LIGHT_UPKEEP": {
"CITY": 23,
"WORKER": 4,
"CART": 10
},
"WOOD_GROWTH_RATE": 1.025,
"MAX_WOOD_AMOUNT": 500,
"CITY_BUILD_COST": 100,
"CITY_ADJACENCY_BONUS": 5,
"RESOURCE_CAPACITY": {
"WORKER": 100,
"CART": 2000
},
"WORKER_COLLECTION_RATE": {
"WOOD": 20,
"COAL": 5,
"URANIUM": 2
},
"RESOURCE_TO_FUEL_RATE": {
"WOOD": 1,
"COAL": 10,
"URANIUM": 40
},
"RESEARCH_REQUIREMENTS": {
"COAL": 50,
"URANIUM": 200
},
"CITY_ACTION_COOLDOWN": 10,
"UNIT_ACTION_COOLDOWN": {
"CART": 3,
"WORKER": 2
},
"MAX_ROAD": 6,
"MIN_ROAD": 0,
"CART_ROAD_DEVELOPMENT_RATE": 0.75,
"PILLAGE_RATE": 0.5
}
}
Loading