diff --git a/kits/fsharp/simple/Bot.sln b/kits/fsharp/simple/Bot.sln new file mode 100644 index 00000000..08e170e7 --- /dev/null +++ b/kits/fsharp/simple/Bot.sln @@ -0,0 +1,37 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.30114.105 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "Bot", "Bot/Bot.fsproj", "{536AC455-03F9-4666-B5DE-98E94B5EBA69}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|Any CPU = Release|Any CPU + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {536AC455-03F9-4666-B5DE-98E94B5EBA69}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {536AC455-03F9-4666-B5DE-98E94B5EBA69}.Debug|Any CPU.Build.0 = Debug|Any CPU + {536AC455-03F9-4666-B5DE-98E94B5EBA69}.Debug|x64.ActiveCfg = Debug|Any CPU + {536AC455-03F9-4666-B5DE-98E94B5EBA69}.Debug|x64.Build.0 = Debug|Any CPU + {536AC455-03F9-4666-B5DE-98E94B5EBA69}.Debug|x86.ActiveCfg = Debug|Any CPU + {536AC455-03F9-4666-B5DE-98E94B5EBA69}.Debug|x86.Build.0 = Debug|Any CPU + {536AC455-03F9-4666-B5DE-98E94B5EBA69}.Release|Any CPU.ActiveCfg = Release|Any CPU + {536AC455-03F9-4666-B5DE-98E94B5EBA69}.Release|Any CPU.Build.0 = Release|Any CPU + {536AC455-03F9-4666-B5DE-98E94B5EBA69}.Release|x64.ActiveCfg = Release|Any CPU + {536AC455-03F9-4666-B5DE-98E94B5EBA69}.Release|x64.Build.0 = Release|Any CPU + {536AC455-03F9-4666-B5DE-98E94B5EBA69}.Release|x86.ActiveCfg = Release|Any CPU + {536AC455-03F9-4666-B5DE-98E94B5EBA69}.Release|x86.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {5FDB8BFE-ED7D-443E-AEBE-CF5AE39EA4E7} + EndGlobalSection +EndGlobal diff --git a/kits/fsharp/simple/Bot/Bot.fs b/kits/fsharp/simple/Bot/Bot.fs new file mode 100644 index 00000000..37d466a8 --- /dev/null +++ b/kits/fsharp/simple/Bot/Bot.fs @@ -0,0 +1,68 @@ +module Bot + +open System +open Lux.GameObjects +open Lux.GameConstants +open Lux + +let takeActions (gameState: Game) : seq = + let player = gameState.Players.[gameState.Id] + let opponent = gameState.Players.[(gameState.Id + 1) % 2] + let gameMap = gameState.GameMap + let resourceTiles = + seq { + for y in 0 .. gameState.GameMap.Height-1 do + for x in 0 .. gameState.GameMap.Width-1 do + let cell = gameMap.GetCell(x, y) + if (cell.HasResource()) then + yield cell + } + seq { + // we iterate over all our units and do something with them + for unit in player.Units do + if (unit.IsWorker() && unit.CanAct()) then + // if the unit is a worker and we have space in cargo, lets find the nearest resource tile and try to mine it + let mutable closestDist = Int32.MaxValue; + let mutable closestResourceTile = None; + if unit.GetCargoSpaceLeft() > 0 then + for resourceTile in resourceTiles do + match resourceTile, resourceTile.Resource with + | _, None -> () + | cell, Some resource -> + match cell, resource.Type with + | _, s when + s = GAME_CONSTANTS.ResourceTypes.Coal && not (player.ResearchedCoal()) -> + () + | _, s when + s = GAME_CONSTANTS.ResourceTypes.Uranium && not (player.ResearchedUranium()) -> + () + | cell, _ -> + let dist = cell.Pos.DistanceTo(unit.Pos); + if (dist < closestDist) then + closestDist <- dist + closestResourceTile <- Some cell + match closestResourceTile with + | None -> () + | Some tile -> + let dir = unit.Pos.DirectionTo(tile.Pos); + // move the unit in the direction towards the closest resource tile's position. + yield unit.Move(dir) + else + // if unit is a worker and there is no cargo space left, and we have cities, lets return to them + if player.Cities.Count > 0 then + let mutable closestDist = Int32.MaxValue; + let mutable closestCityTile: option = None; + for city in player.Cities.Values do + for cityTile in city.CityTiles do + let dist = cityTile.Pos.DistanceTo(unit.Pos); + if (dist < closestDist) then + closestCityTile <- Some cityTile + closestDist <- dist + match closestCityTile with + | None -> () + | Some tile -> + let dir = unit.Pos.DirectionTo(tile.Pos); + yield unit.Move(dir); + // you can add debug annotations using the static methods of the Annotate class. + // yield Annotate.Circle(0, 0); + } \ No newline at end of file diff --git a/kits/fsharp/simple/Bot/Bot.fsproj b/kits/fsharp/simple/Bot/Bot.fsproj new file mode 100644 index 00000000..4e10da99 --- /dev/null +++ b/kits/fsharp/simple/Bot/Bot.fsproj @@ -0,0 +1,24 @@ + + + Exe + net5.0 + fsharp_simple + true + 3390;$(WarnOn) + + + + PreserveNewest + + + + + + + + + + + + + \ No newline at end of file diff --git a/kits/fsharp/simple/Bot/Lux/Agent.fs b/kits/fsharp/simple/Bot/Lux/Agent.fs new file mode 100644 index 00000000..cf7179f8 --- /dev/null +++ b/kits/fsharp/simple/Bot/Lux/Agent.fs @@ -0,0 +1,117 @@ +module Lux.Agent + +open System +open GameObjects +open System.Collections.Generic + +let private updateUnfold (gameState:Game) : option = + let updateInfo = Console.ReadLine() + if updateInfo = Constants.INPUT_CONSTANTS.DONE then + None + else + let updates = updateInfo.Split(" "); + let inputIdentifier : string = updates.[0]; + match inputIdentifier with + | s when s = Constants.INPUT_CONSTANTS.RESEARCH_POINTS -> + let team: int = Int32.Parse(updates.[1]); + let newPoints = Int32.Parse(updates.[2]) + gameState.Players.[team].ResearchPoints <- newPoints + Some(gameState, gameState) + | s when s = Constants.INPUT_CONSTANTS.RESOURCES -> + let r_type = updates.[1]; + let x = Int32.Parse(updates.[2]); + let y = Int32.Parse(updates.[3]); + let amt = (int)(Double.Parse(updates.[4])) + gameState.GameMap._setResource(r_type, x, y, amt) + Some(gameState, gameState) + | s when s = Constants.INPUT_CONSTANTS.UNITS -> + let unittype = Int32.Parse(updates.[1]); + let team = Int32.Parse(updates.[2]); + let unitid = updates.[3]; + let x = Int32.Parse(updates.[4]); + let y = Int32.Parse(updates.[5]); + let cooldown = Double.Parse(updates.[6]); + let wood = Int32.Parse(updates.[7]); + let coal = Int32.Parse(updates.[8]); + let uranium = Int32.Parse(updates.[9]); + let unit = new Unit(team, Constants.ParseUnitType unittype, unitid, x, y, cooldown, wood, coal, uranium); + let newGameState = + match gameState.Players with + | players -> + players.[team].Units <- unit::players.[team].Units + { gameState with + Players = players + } + | _ -> failwith "More than two players present, do ghosts exits?" + Some(newGameState, newGameState) + | s when s = Constants.INPUT_CONSTANTS.CITY -> + let team = Int32.Parse(updates.[1]); + let cityid = updates.[2]; + let fuel = Double.Parse(updates.[3]); + let lightUpkeep = Double.Parse(updates.[4]); + gameState.Players.[team].Cities.Add(cityid, new City(team, cityid, fuel, lightUpkeep)); + Some(gameState, gameState) + | s when s = Constants.INPUT_CONSTANTS.CITY_TILES -> + let team = Int32.Parse(updates.[1]); + let cityid = updates.[2]; + let x = Int32.Parse(updates.[3]); + let y = Int32.Parse(updates.[4]); + let cooldown = Double.Parse(updates.[5]); + let city = gameState.Players.[team].Cities.Item cityid + let citytile = city._add_city_tile(x, y, cooldown); + gameState.GameMap.GetCell(x, y).Citytile <- citytile; + gameState.Players.[team].CityTileCount <- gameState.Players.[team].CityTileCount + 1; + Some(gameState, gameState) + | s when s = Constants.INPUT_CONSTANTS.ROADS -> + let x = Int32.Parse(updates.[1]); + let y = Int32.Parse(updates.[2]); + let road = Double.Parse(updates.[3]); + let cell = gameState.GameMap.GetCell(x, y); + cell.Road <- road; + Some(gameState, gameState) + | _ -> failwith "Unknown input string" + +type IGameStateIO = + abstract member update : unit -> Game + abstract member endTurn: unit -> unit + +type GameStateIO = + { + gameState : Game + } + static member initialize() : Game = + let id = Int32.Parse(Console.ReadLine()) + let mapInfo: string = Console.ReadLine() + let mapInfoSplit: string[] = mapInfo.Split(" ") + let mapWidth = Int32.Parse(mapInfoSplit.[0]) + let mapHeight = Int32.Parse(mapInfoSplit.[1]) + let map = new GameMap(mapWidth, mapHeight) + let gameState : Game = + { + Id = id + GameMap = map; + Turn = 0; + Players = [Player(0); Player(1)] + } + gameState + + member self.update() = + let newPlayer0 = + Player(0, [], new Dictionary(), self.gameState.Players.[0].CityTileCount) + let newPlayer1 = + Player(1, [], new Dictionary(), self.gameState.Players.[1].CityTileCount) + let newGameState = + { self.gameState with + Turn = self.gameState.Turn + 1; + Players = [newPlayer0; newPlayer1] + GameMap = GameMap(self.gameState.GameMap.Height, self.gameState.GameMap.Width) + } + |> Seq.unfold (fun state -> + updateUnfold state + ) + |> Seq.last + { self with gameState = newGameState } + + /// Ends turn + member self.endTurn() = + Console.WriteLine("D_FINISH") diff --git a/kits/fsharp/simple/Bot/Lux/Annotate.fs b/kits/fsharp/simple/Bot/Lux/Annotate.fs new file mode 100644 index 00000000..78eefbfd --- /dev/null +++ b/kits/fsharp/simple/Bot/Lux/Annotate.fs @@ -0,0 +1,18 @@ +module Lux.Annotate + +let Circle(x: int, y: int) : string = + $"dc {x} {y}" + +let x(x: int, y: int) : string = + $"dx {x} {y}" + +let Line(x1: int, y1: int, x2: int, y2: int) : string = + $"dl {x1} {y1} {x2} {y2}" + +/// text at cell on map +let Text (x: int, y: int, message: string, fontsize: option) : string = + $"dt {x} {y} '{message}' {defaultArg fontsize 16}" + +/// text besides map +let Sidetext(message: string) : string = + $"dst '{message}'" diff --git a/kits/fsharp/simple/Bot/Lux/Constants.fs b/kits/fsharp/simple/Bot/Lux/Constants.fs new file mode 100644 index 00000000..173c3129 --- /dev/null +++ b/kits/fsharp/simple/Bot/Lux/Constants.fs @@ -0,0 +1,44 @@ +module Lux.Constants + +module INPUT_CONSTANTS = + let RESEARCH_POINTS = "rp" + let RESOURCES = "r" + let UNITS = "u" + let CITY = "c" + let CITY_TILES = "ct" + let ROADS = "ccd" + let DONE = "D_DONE" + +type DIRECTIONS = + | NORTH + | WEST + | SOUTH + | EAST + | CENTER + override this.ToString() = + match this with + | NORTH -> "n" + | WEST -> "w" + | SOUTH -> "s" + | EAST -> "e" + | CENTER -> "c" + +type UNIT_TYPES = + | WORKER = 0 + | CART = 1 + +let ParseUnitType value = + match value with + | 0 -> UNIT_TYPES.WORKER + | 1 -> UNIT_TYPES.CART + | _ -> failwith "Unexpected unit type encountered." + +type RESOURCE_TYPES = + | WOOD + | URANIUM + | COAL + override this.ToString() = + match this with + | WOOD -> "wood" + | URANIUM -> "uranium" + | COAL -> "coal" diff --git a/kits/fsharp/simple/Bot/Lux/GameConstants.fs b/kits/fsharp/simple/Bot/Lux/GameConstants.fs new file mode 100644 index 00000000..9c5e190f --- /dev/null +++ b/kits/fsharp/simple/Bot/Lux/GameConstants.fs @@ -0,0 +1,75 @@ +namespace Lux + +open FSharp.Data + +module GameConstants = + type private jsonFormat = JsonProvider<"./Lux/game_constants.json"> + let private STATIC_CONST = """ +{ + "UNIT_TYPES": { + "WORKER": 0, + "CART": 1 + }, + "RESOURCE_TYPES": { + "WOOD": "wood", + "COAL": "coal", + "URANIUM": "uranium" + }, + "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 + } +} +""" + let GAME_CONSTANTS = + let path = "./Lux/game_constants.json" + let content = + if System.IO.File.Exists(path) then + System.IO.File.ReadAllText(path) + else + STATIC_CONST + jsonFormat.Parse(content) \ No newline at end of file diff --git a/kits/fsharp/simple/Bot/Lux/GameObjects.fs b/kits/fsharp/simple/Bot/Lux/GameObjects.fs new file mode 100644 index 00000000..0dfc80c5 --- /dev/null +++ b/kits/fsharp/simple/Bot/Lux/GameObjects.fs @@ -0,0 +1,218 @@ +module Lux.GameObjects + +open Constants +open System.Collections.Generic +open GameConstants + +// GameMap: +type Resource(r_type: string, amount: int) = + member self.Type = r_type + member self.amount = amount + +type Position(_x, _y) = + member self.x = _x + member self.y = _y + static member (-) (self: Position, pos: Position) : int = + abs(pos.x - self.x) + abs(pos.y - self.y) + + /// Returns Manhattan (L1/grid) distance to pos + member self.DistanceTo(pos: Position) = + self - pos + + member self.IsAdjacent(pos: Position) = + (self - pos) <= 1 + + static member op_Equality (self: Position, pos: Position) : bool = + self.x = pos.x && self.y = pos.y + + member self.Translate(direction, units): Position = + match direction with + | NORTH -> + Position(self.x, self.y - units) + | EAST -> + Position(self.x + units, self.y) + | SOUTH -> + Position(self.x, self.y + units) + | WEST -> + Position(self.x - units, self.y) + | CENTER -> + Position(self.x, self.y) + + /// Return closest position to target_pos from this position + member self.DirectionTo(target_pos: 'Position') : DIRECTIONS = + let check_dirs = [ + NORTH; + EAST; + SOUTH; + WEST; + ] + let closest_dist_init, closest_dir_init = (self.DistanceTo(target_pos), DIRECTIONS.CENTER) + let closest_dist, closest_dir = + check_dirs + |> List.fold (fun (closest_dist, closest_dir) direction -> + let newpos = self.Translate(direction, 1) + let dist = target_pos.DistanceTo(newpos) + if dist < closest_dist then + dist, direction + else + closest_dist, closest_dir + ) (closest_dist_init, closest_dir_init) + closest_dir + + override self.ToString() = + sprintf "(%d, %d)" self.x self.y + +// GameObjects: +type CityTile(teamid, cityid, x, y, cooldown: float) = + member self.Cityid: string = cityid + member self.Team = teamid + member self.Pos = Position(x, y) + member self.Cooldown = cooldown + + /// Whether or not this unit can research or build + member self.CanAct() : bool = + self.Cooldown < 1.0 + + /// returns command to ask this tile to research this turn + member self.Research() : string = + sprintf "r %d %d" self.Pos.x self.Pos.y + + /// returns command to ask this tile to build a worker this turn + member self.BuildWorker() : string = + sprintf "bw %d %d" self.Pos.x self.Pos.y + + /// returns command to ask this tile to build a cart this turn + member self.BuildCart() : string = + sprintf "bc %d %d" self.Pos.x self.Pos.y + +type Cell(x, y) = + member self.Pos: Position = Position(x, y) + member val Resource: option = None with get, set + member val Citytile: option = None with get, set + member val Road = 0.0 with get, set + member self.HasResource() = + match self.Resource with + | None -> + false + | Some resource -> + resource.amount > 0 + +type City(teamid: int, cityid: string, fuel: float, light_upkeep_init: float) = + member self.CityId = cityid + member self.Team = teamid + member self.Fuel = fuel + member val CityTiles: List = new List() with get, set + member self.LightUpkeep = light_upkeep_init + member self._add_city_tile(x, y, cooldown) = + let ct = CityTile(self.Team, self.CityId, x, y, cooldown) + self.CityTiles.Add(ct) + Some ct + member self.GetLightUpkeep() = + self.LightUpkeep + +type GameMap(width, height) = + member self.Height = height + member self.Width = width + // member self.map: List[List[Cell]] = [None] * height + member val Map: Cell[,] = Array2D.init width height (fun x y -> Cell(x,y)) + // for y in 0 .. self.height) do + // self.map.[y] = [None] * width + // for x in range(0, self.width) do + // self.map[y][x] = Cell(x, y) + + member self.GetCellByPos(pos: Position) : Cell = + self.Map.[pos.x, pos.y] + + member self.GetCell(x, y) : Cell = + self.Map.[x, y] + + /// do not use this function, this is for internal tracking of state + member self._setResource(r_type, x, y, amount) = + self.GetCell(x, y).Resource <- Some(Resource(r_type, amount)) + +type Cargo(wood, coal, uranium) = + member self.Wood = wood + member self.Coal = coal + member self.Uranium = uranium + new() = + Cargo(0, 0, 0) + + override self.ToString() : string = + sprintf "Cargo | Wood: %d, Coal: %d, Uranium: %d" self.Wood self.Coal self.Uranium + +type Unit(teamid, uType, unitid, x, y, cooldown, wood, coal, uranium) = + member val Pos = Position(x, y) + member val Team: int = teamid with get, set + member val Id: string = unitid with get, set + member self.Cooldown = cooldown + member self.Cargo : Cargo = new Cargo(wood, coal, uranium) + member private self.UnitType = uType + member self.IsWorker() : bool = + self.UnitType = UNIT_TYPES.WORKER + + member self.IsCart() : bool = + self.UnitType = UNIT_TYPES.CART + + /// get cargo space left in this unit + member self.GetCargoSpaceLeft() = + let spaceused = self.Cargo.Wood + self.Cargo.Coal + self.Cargo.Uranium + if self.UnitType = UNIT_TYPES.WORKER then + GAME_CONSTANTS.Parameters.ResourceCapacity.Worker - spaceused + else + GAME_CONSTANTS.Parameters.ResourceCapacity.Cart - spaceused + + /// whether or not the unit can build where it is right now + member self.CanBuild(gameMap: GameMap) : bool = + let cell = gameMap.GetCellByPos(self.Pos) + not (cell.HasResource()) + && self.CanAct() + && (self.Cargo.Wood + self.Cargo.Coal + self.Cargo.Uranium) >= GAME_CONSTANTS.Parameters.CityBuildCost + + /// whether or not the unit can move or not. This does not check for potential collisions into other units or enemy cities + member self.CanAct() : bool = + self.Cooldown < 1.0 + + /// return the command to move unit in the given direction + member self.Move(dir: DIRECTIONS) : string = + sprintf "m %s %s" self.Id (dir.ToString()) + + /// return the command to transfer a resource from a source unit to a destination unit as specified by their ids + member self.Transfer(dest_id, resourceType, amount) : string = + sprintf "t %s %d %d %d" self.Id dest_id resourceType amount + + /// return the command to build a city right under the worker + member self.BuildCity() : string = + sprintf "bcity %s" self.Id + + /// return the command to pillage whatever is underneath the worker + member self.Pillage() : string = + sprintf "p %s" self.Id + +type Player(team, units, cities, tilecount) = + new(teamId) = + Player(teamId, [], new Dictionary(), 0) + member val Team: int = team with get, set + member val ResearchPoints = 0 with get, set + member val Units: list = units with get, set + member val Cities: Dictionary = cities + member val CityTileCount: int = tilecount with get, set + member self.ResearchedCoal() : bool = + self.ResearchPoints >= GAME_CONSTANTS.Parameters.ResearchRequirements.Coal + member self.ResearchedUranium() : bool = + self.ResearchPoints >= GAME_CONSTANTS.Parameters.ResearchRequirements.Uranium + +type Game = + { + Id: int + Turn: int + GameMap: GameMap + Players: list + } + member private self._reset_player_states() = + { self with + Players = + [ + Player(0, [], new Dictionary(), 0); + Player(1, [], new Dictionary(), 0); + ] + } \ No newline at end of file diff --git a/kits/fsharp/simple/Bot/Lux/game_constants.json b/kits/fsharp/simple/Bot/Lux/game_constants.json new file mode 100644 index 00000000..9f750757 --- /dev/null +++ b/kits/fsharp/simple/Bot/Lux/game_constants.json @@ -0,0 +1,59 @@ +{ + "UNIT_TYPES": { + "WORKER": 0, + "CART": 1 + }, + "RESOURCE_TYPES": { + "WOOD": "wood", + "COAL": "coal", + "URANIUM": "uranium" + }, + "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 + } +} diff --git a/kits/fsharp/simple/Bot/Main.fs b/kits/fsharp/simple/Bot/Main.fs new file mode 100644 index 00000000..963ce9fb --- /dev/null +++ b/kits/fsharp/simple/Bot/Main.fs @@ -0,0 +1,21 @@ +open System +open Lux.Agent + +[] +let main args = + let agent: GameStateIO = { gameState = GameStateIO.initialize() } + while true do + let game = agent.update() + + (* Set actions *) + let actions : seq = + Bot.takeActions game.gameState + + (* AI Code Goes Above! *) + + (* Do not edit *) + let command = + String.concat "," actions + Console.WriteLine(command) + agent.endTurn() + 0 \ No newline at end of file