Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding a starter kit for fsharp #162

Draft
wants to merge 5 commits into
base: master
Choose a base branch
from
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
37 changes: 37 additions & 0 deletions kits/fsharp/simple/Bot.sln
Original file line number Diff line number Diff line change
@@ -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
68 changes: 68 additions & 0 deletions kits/fsharp/simple/Bot/Bot.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
module Bot

open System
open Lux.GameObjects
open Lux.GameConstants
open Lux

let takeActions (gameState: Game) : seq<string> =
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<CityTile> = 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);
}
24 changes: 24 additions & 0 deletions kits/fsharp/simple/Bot/Bot.fsproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net5.0</TargetFramework>
<RootNamespace>fsharp_simple</RootNamespace>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<WarnOn>3390;$(WarnOn)</WarnOn>
</PropertyGroup>
<ItemGroup>
<Content Include="Lux\game_constants.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Compile Include="Lux\Annotate.fs" />
<Compile Include="Lux\Constants.fs" />
<Compile Include="Lux\GameConstants.fs" />
<Compile Include="Lux\GameObjects.fs" />
<Compile Include="Lux\Agent.fs" />
<Compile Include="Bot.fs" />
<Compile Include="Main.fs" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="FSharp.Data" Version="4.2.4" />
</ItemGroup>
</Project>
117 changes: 117 additions & 0 deletions kits/fsharp/simple/Bot/Lux/Agent.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
module Lux.Agent

open System
open GameObjects
open System.Collections.Generic

let private updateUnfold (gameState:Game) : option<Game * Game> =
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<string, City>(), self.gameState.Players.[0].CityTileCount)
let newPlayer1 =
Player(1, [], new Dictionary<string, City>(), 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")
18 changes: 18 additions & 0 deletions kits/fsharp/simple/Bot/Lux/Annotate.fs
Original file line number Diff line number Diff line change
@@ -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<int>) : string =
$"dt {x} {y} '{message}' {defaultArg fontsize 16}"

/// text besides map
let Sidetext(message: string) : string =
$"dst '{message}'"
44 changes: 44 additions & 0 deletions kits/fsharp/simple/Bot/Lux/Constants.fs
Original file line number Diff line number Diff line change
@@ -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"
75 changes: 75 additions & 0 deletions kits/fsharp/simple/Bot/Lux/GameConstants.fs
Original file line number Diff line number Diff line change
@@ -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)
Loading