Skip to content

Instantly share code, notes, and snippets.

@DanielVF
Last active January 28, 2020 01:07
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save DanielVF/e7a481c402b5e6dbac1a91a2251dfb09 to your computer and use it in GitHub Desktop.
Save DanielVF/e7a481c402b5e6dbac1a91a2251dfb09 to your computer and use it in GitHub Desktop.
Nim engine, no documentation
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)
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-"&paramStr(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
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(" ")
@narimiran
Copy link

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.

type # type block, no need to repeat `type`
  Entity* = ref object of RootObj
    id*, x*, y*: int # all defined on the same line, if you want
    cell*: Cell

  Shipyard* = ref object of Entity

  Ship* = ref object of Entity
    halite*: int
let # let block, no need to repeat `let` on each line
  ox = c1.x - c2.x
  oy = c1.y - c2.y
  halfx = g.width div 2
  halfy = g.height div 2
type
  Cell* = ref object of RootObj
    i*, x*, y*, halite*: int
    neighbors*: array[Direction, Cell] # use enum directly, if you want
for i 0 ..< numShips: # for i in countup(0, numShips-1)

for i in 1 .. g.numPlayers: # for i in countup(1, g.numPlayers)
proc newPosition*(x, y: int): Position # grouping of arguments of the same type

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment