Last active
January 28, 2020 01:07
-
-
Save DanielVF/e7a481c402b5e6dbac1a91a2251dfb09 to your computer and use it in GitHub Desktop.
Nim engine, no documentation
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import sets | |
import engine | |
import strutils | |
import math | |
# import log | |
type | |
Atc* = ref object of RootObj | |
available*: HashSet[Ship] | |
occupied*: seq[bool] | |
g: Game | |
orders*: seq[string] | |
proc newAtc*(g: Game): Atc = | |
result = new Atc | |
result.available.init(nextPowerOfTwo(g.me.ships.len)) | |
for ship in g.me.ships: | |
result.available.incl(ship) | |
result.occupied = newSeq[bool](g.width * g.height) | |
result.g = g | |
result.orders = newSeq[string]() | |
for player in g.players: | |
if player.id == g.me.id: | |
continue | |
for ship in player.ships: | |
if g.dist(ship.cell, g.me.shipyard.cell) <= 1: | |
continue | |
if ship.halite > 800: | |
continue | |
result.occupied[ship.cell.i] = true | |
iterator availableShips*(a: Atc): Ship = | |
## Returns a list of all ships that have not had a move order issued yet | |
for ship in a.g.me.ships: | |
if a.available.contains(ship): | |
yield ship | |
proc canMove*(a: Atc, dest: Cell): bool = | |
# assert dest != nil, "Dest should not be null" | |
return a.occupied[dest.i] == false | |
proc empty*(a: Atc, dest: Cell): bool = | |
# assert dest != nil, "Dest should not be null" | |
return a.occupied[dest.i] == false | |
proc mark*(a: Atc, c: Cell) = | |
a.occupied[c.i] = true | |
proc mark*(a: Atc, e: Entity) = | |
a.occupied[e.cell.i] = true | |
proc orderMove*(a: Atc, s:Ship, d: Direction) = | |
# echo "Marking Occupied "&s.cell.adjacent(d).str | |
a.mark(s.cell.adjacent(d)) | |
a.available.excl(s) | |
let directionString = case d: | |
of Still: "o" | |
of North: "n" | |
of East: "e" | |
of South: "s" | |
of West: "w" | |
a.orders.add("m " & strutils.intToStr(s.id) & " " & directionString) | |
proc orderBuildShip*(a: Atc) = | |
let shipyardPos = a.g.me.shipyard.cell | |
if a.g.me.halite >= 1000 and a.empty(shipyardPos): | |
a.occupied[shipyardPos.i] = true | |
a.orders.add("g") | |
proc orderBuildDropoff*(a: Atc, s:Ship) = | |
if a.g.me.halite >= 4000: | |
a.available.excl(s) | |
a.orders.add("c " & strutils.intToStr(s.id)) | |
proc move*(a: Atc, s: Ship, d: Direction, msg = "") = | |
## Standard command to issue a move order to a ship. | |
## Will prevent a move to a square that will be occupied at the end of the turn | |
let c = s.cell.adjacent(d) | |
if a.canMove(c): | |
# c.log(msg) | |
a.orderMove(s, d) | |
return | |
for d in [South, North, East, West,Still]: | |
let c = s.cell.adjacent(d) | |
if a.canMove(c): | |
# c.log(msg) | |
a.orderMove(s, d) | |
return | |
proc move*(a: Atc, s: Ship, c: Cell, msg = "") = | |
let offset = a.g.offset(s.cell, c) | |
let d = | |
if offset.y > 0 and a.empty(s.cell.adjacent(North)) : North | |
elif offset.y < 0 and a.empty(s.cell.adjacent(South)) : South | |
elif offset.x < 0: East | |
elif offset.x > 0: West | |
else: Still | |
a.move(s, d, msg) | |
proc markFuelStuckShips*(a: Atc) = | |
for ship in a.availableShips: | |
if ship.halite < ship.cell.halite div 10: | |
a.orderMove(ship, Still) | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import json | |
import parseutils | |
import strutils | |
import hashes | |
import sequtils | |
import random | |
import os | |
# let logname:string = if (paramCount() >= 1): paramStr(1) | |
# else: "bot" | |
# let o = open("output-"¶mStr(1)&".txt", fmWrite) | |
# proc log*(msg: string) = | |
# o.writeLine(msg) | |
type | |
Direction* = enum | |
North = 0, East = 1, South = 2, West = 3, Still = 4 | |
type Position* = object of RootObj | |
x*: int | |
y*: int | |
proc newPosition*(x:int, y: int) : Position = | |
result.x = x | |
result.y = y | |
type | |
Cell* = ref object of RootObj | |
i*: int | |
x*: int | |
y*: int | |
halite*: int | |
neighbors*: array[4, Cell] | |
proc adjacent*(c: Cell, d: Direction) : Cell = | |
if d == Still: | |
return c | |
return c.neighbors[int(d)] | |
proc `$`*(c: Cell): string = | |
result = "<Cell "&intToStr(c.i)&" "&intToStr(c.x)&"x"&intToStr(c.y)&" halite:"&intToStr(c.halite)&">" | |
type | |
Entity* = ref object of RootObj | |
id*: int | |
x*: int | |
y*: int | |
cell*: Cell | |
type | |
Shipyard* = ref object of Entity | |
type | |
Ship* = ref object of Entity | |
halite*: int | |
proc str*(s: Ship): string = | |
result = "<Ship "&intToStr(s.id)&" "&intToStr(s.x)&"x"&intToStr(s.y)&" carry:"&intToStr(s.halite)&" on:"&intToStr(s.cell.halite)&">" | |
type | |
Dropoff* = ref object of Entity | |
type | |
Player* = ref object of RootObj | |
id*: int | |
ships*: seq[Ship] | |
dropoffs*: seq[Dropoff] | |
shipyard*: Shipyard | |
halite*: int | |
type | |
Network* = ref object of RootObj | |
line*: string | |
offset: int | |
method nextLine*(net: Network) = | |
net.line = readLine(stdin) | |
# log(net.line) | |
net.offset = 0 | |
proc readInt*(net: Network): int = | |
net.offset += 1 + parseutils.parseInt(net.line, result, net.offset) | |
type | |
StringNetwork* = ref object of Network | |
lines: seq[string] | |
lineIndex: int | |
proc newStringNetwork*(str: string): StringNetwork = | |
result = new StringNetwork | |
result.lines = toSeq(str.splitLines) | |
result.lineIndex = 0 | |
method nextLine*(net: StringNetwork) = | |
net.line = net.lines[net.lineIndex] | |
net.offset = 0 | |
net.lineIndex += 1 | |
type | |
Game* = ref object of RootObj | |
cells*: seq[Cell] | |
width*: int | |
height*: int | |
turn*: int | |
turnMax*: int | |
numPlayers*: int | |
players*: seq[Player] | |
me*: Player | |
proc cellForEntity*(g: Game, e: Entity): Cell = | |
let i = e.x + e.y * g.width | |
return g.cells[i] | |
proc adjacentRaw(g: Game, x: int, y: int, d:Direction) : Cell = | |
var x = x | |
var y = y | |
case d: | |
of North: | |
if y == 0: | |
y = g.height - 1 | |
else: | |
y -= 1 | |
of East: | |
if x == g.width - 1: | |
x = 0 | |
else: | |
x += 1 | |
of South: | |
if y == g.height - 1: | |
y = 0 | |
else: | |
y += 1 | |
of West: | |
if x == 0: | |
x = g.width - 1 | |
else: | |
x -= 1 | |
of Still: | |
x = x # Do nothing | |
let i = x + y * g.width | |
return g.cells[i] | |
proc newGameFromNet*(net: Network) : Game = | |
var g = new Game | |
# JSON | |
net.nextLine() | |
let jsonNode = parseJson(net.line) | |
g.turnMax = jsonNode["MAX_TURNS"].getInt() | |
# Players | |
net.nextLine() | |
let numPlayers = net.readInt() | |
g.numPlayers = int(numPlayers) | |
let myId = net.readInt() | |
# Shipyards | |
g.players = newSeq[Player](g.numPlayers) | |
for i in countup(1, g.numPlayers): | |
net.nextLine() | |
let player = new Player | |
player.id = net.readInt() | |
let shipyard = new Shipyard | |
shipyard.x = net.readInt() | |
shipyard.y = net.readInt() | |
player.shipyard = shipyard | |
g.players[player.id] = player | |
g.me = g.players[myId] | |
# Map size | |
net.nextLine() | |
let width = net.readInt() | |
let height = net.readInt() | |
g.width = width | |
g.height = height | |
# Map Halite | |
newSeq(g.cells, width * height) | |
for y in countup(0, height-1): | |
net.nextLine() | |
for x in countup(0, width-1): | |
let i = y * width + x | |
let c = Cell() | |
c.x = x | |
c.y = y | |
c.i = i | |
c.halite = net.readInt() | |
g.cells[i] = c | |
# Link neighbors | |
for y in countup(0, height-1): | |
for x in countup(0, width-1): | |
let i = x + y * width | |
let cell = g.cells[i] | |
cell.neighbors[int(North)] = g.adjacentRaw(x,y,North) | |
cell.neighbors[int(East)] = g.adjacentRaw(x,y,East) | |
cell.neighbors[int(South)] = g.adjacentRaw(x,y,South) | |
cell.neighbors[int(West)] = g.adjacentRaw(x,y,West) | |
for p in g.players: | |
p.shipyard.cell = g.cellForEntity(p.shipyard) | |
result = g | |
proc updateFromNet*(g: Game, net: Network) = | |
net.nextLine() | |
g.turn = net.readInt() | |
for _ in countup(0, int(g.numPlayers-1)): | |
net.nextLine() | |
let playerId = net.readInt() | |
assert playerId < g.numPlayers, "Got out of bounds player ID " & strutils.intToStr(playerId) | |
let player = g.players[playerId] | |
let numShips = net.readInt() # num_ships : number of alive ships controlled by player id | |
let numDropoffs = net.readInt() # num_dropoffs : number of dropoffs controlled by player id (notice: shipyard not included) | |
player.halite = net.readInt() # halite : amount of halite in player id bank (i.e. deposited - spent) | |
player.ships = newSeq[Ship](numShips) | |
for i in countup(0, numShips-1): | |
net.nextLine() | |
player.ships[i] = new Ship | |
let ship = player.ships[i] | |
ship.id = net.readInt() # ship_id : unique ship identifier | |
ship.x = net.readInt() # x y: position of the ship | |
ship.y = net.readInt() | |
ship.cell = g.cellForEntity(ship) | |
ship.halite = net.readInt() # halite : amount of halite carried by the ship (max_cargo is 1000) | |
player.dropoffs = newSeq[Dropoff](numDropoffs) | |
for i in countup(0, numDropoffs-1): | |
net.nextLine() | |
player.dropoffs[i] = new Dropoff | |
let dropoff = player.dropoffs[i] | |
dropoff.id = net.readInt() # ship_id : unique dropoff identifier | |
dropoff.x = net.readInt() # x y: position of the dropoff | |
dropoff.y = net.readInt() | |
dropoff.cell = g.cellForEntity(dropoff) | |
net.nextLine() | |
for _ in countup(1, net.readInt()): | |
net.nextLine() | |
let i = net.readInt() + net.readInt() * g.width | |
g.cells[i].halite = net.readInt() | |
proc offset*(g: Game, c1: Position, c2: Position): Position = | |
let ox = c1.x - c2.x | |
let oy = c1.y - c2.y | |
let halfx = g.width div 2 | |
let halfy = g.height div 2 | |
result.x = if ox < halfx and ox > halfx * -1 : | |
ox | |
elif ox < 0: # Eastward wrapping | |
g.width + ox | |
else: # Westward wrapping | |
(g.width - ox) * -1 | |
result.y = if oy < halfy and oy > halfy * -1 : | |
oy | |
elif oy < 0: # Northward wrapping | |
g.height + oy | |
else: # South wrapping | |
(g.height - oy) * -1 | |
proc offset*(g: Game, c1: Cell, c2: Cell): Position = | |
let ox = c1.x - c2.x | |
let oy = c1.y - c2.y | |
let halfx = g.width div 2 | |
let halfy = g.height div 2 | |
result.x = if ox < halfx and ox > halfx * -1 : | |
ox | |
elif ox < 0: # Eastward wrapping | |
g.width + ox | |
else: # Westward wrapping | |
(g.width - ox) * -1 | |
result.y = if oy < halfy and oy > halfy * -1 : | |
oy | |
elif oy < 0: # Northward wrapping | |
g.height + oy | |
else: # South wrapping | |
(g.height - oy) * -1 | |
proc offset*(g: Game, e1: Entity, c2: Cell): Position = | |
result = g.offset(e1.cell, c2) | |
proc dist*(g: Game, c1: Cell, c2: Cell): int = | |
let offset = g.offset(c1, c2) | |
result = abs(offset.x) + abs(offset.y) | |
proc dist*(g: Game, c1: Position, c2: Position): int = | |
let offset = g.offset(c1, c2) | |
result = abs(offset.x) + abs(offset.y) | |
proc `$`*(p:Position) : string = | |
result = "<Vector "&intToStr(p.x)&"x"&intToStr(p.y)&">" | |
# Util | |
proc hash*(ship: Ship): Hash = | |
result = ship.id | |
proc hash*(c: Cell): Hash = | |
result = c.i |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import engine | |
let net = Network() | |
let g : Game = newGameFromNet(net) | |
echo "MyBot 377" | |
while true: | |
updateFromNet(g, net) | |
let atc = newAtc(g) | |
atc.markFuelStuckShips() | |
// Begin your code | |
if g.me.ships.len < 0: | |
atc.orderBuildShip() | |
for ship in in atc.availableShips: | |
atc.move(ship, North) | |
// End turn | |
echo atc.orders.join(" ") |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Just a few comments regarding "idiomatic Nim", with some stuff that you maybe didn't know it is possible. A continuation of our IRC discussion.