Skip to content

Instantly share code, notes, and snippets.

@peterhellberg
Last active January 31, 2020 20:11
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save peterhellberg/e1709cb9ee9e87f32ae901faa8e8c323 to your computer and use it in GitHub Desktop.
Save peterhellberg/e1709cb9ee9e87f32ae901faa8e8c323 to your computer and use it in GitHub Desktop.
Coders of the Caribbean in Go
package main
import (
"encoding/json"
"fmt"
"image"
"io"
"math"
"os"
"strings"
)
const (
BARREL = "BARREL"
CANNONBALL = "CANNONBALL"
FIRE = "FIRE"
MINE = "MINE"
MOVE = "MOVE"
SHIP = "SHIP"
SLOWER = "SLOWER"
WAIT = "WAIT"
)
type Game struct {
Writer io.Writer `json:"-"`
TurnCount int
Outputs []string
}
type Input struct {
ShipCount int
EntityCount int
Entities []Entity
Barrels []Barrel
Cannonballs []Cannonball
Mines []Mine
Ships []Ship
Enemies []Ship
}
type (
Barrel Entity
Cannonball Entity
Mine Entity
Ship Entity
)
type Entity struct {
ID int
Type string
image.Point
Arg1 int
Arg2 int
Arg3 int
Arg4 int
}
type Target struct {
Distance int
Amount int
image.Point
}
func (ship Ship) Output(g *Game, input Input) string {
if g.TurnCount > 1 {
if !strings.HasPrefix(g.Outputs[len(g.Outputs)-1], "FIRE") {
for _, enemy := range input.Enemies {
if ship.Distance(enemy.Point) < 5 {
return ship.Fire(enemy.Point)
}
}
}
}
t := ship.Target(input)
switch t.Point {
case image.ZP:
return SLOWER
default:
return ship.Move(t.Point)
}
}
func (ship Ship) Distance(target image.Point) int {
return hexDistance(ship.Point, target)
}
func (ship Ship) Fire(target image.Point) string {
return fmt.Sprintf("%s %d %d", FIRE, target.X, target.Y)
}
func (ship Ship) Move(target image.Point) string {
return fmt.Sprintf("%s %d %d", MOVE, target.X, target.Y)
}
func (ship Ship) Target(input Input) Target {
closest := Target{Distance: 1000}
var highest Target
for _, b := range input.Barrels {
distance := ship.Distance(b.Point)
if distance < ship.Distance(closest.Point) || closest.Distance > 100 {
closest.Distance = distance
closest.Amount = b.Arg1
closest.Point = b.Point
}
if b.Arg1 > highest.Amount {
highest.Distance = distance
highest.Amount = b.Arg1
highest.Point = b.Point
}
}
dump(obj{
"Closest": closest,
"Highest": highest,
})
if input.ShipCount > 1 {
if ship.ID > 0 {
return closest
}
}
if closest.Distance < 10 {
return closest
}
return highest
}
func main() {
game := &Game{Writer: os.Stdout}
for {
game.Turn(scanInput())
}
}
func (g *Game) Turn(input Input) {
g.TurnCount++
dump(g)
for _, ship := range input.Ships {
o := ship.Output(g, input)
g.Outputs = append(g.Outputs, o)
output(g.Writer, o)
}
}
func output(w io.Writer, s string) {
fmt.Fprintln(w, s)
}
func debug(format string, a ...interface{}) {
fmt.Fprintf(os.Stderr, format+"\n", a...)
}
type obj map[string]interface{}
func dump(v interface{}) {
enc := json.NewEncoder(os.Stderr)
enc.SetIndent("", " ")
enc.Encode(v)
}
func abs(x int) int {
if x < 0 {
return -x
}
return x
}
func hexDistance(start, dest image.Point) int {
if start.X == dest.Y {
return abs(dest.Y - start.Y)
} else if start.Y == dest.Y {
return abs(dest.X - start.X)
} else {
d := image.Pt(abs(dest.X-start.X), abs(dest.Y-start.Y))
if start.Y < dest.Y {
return d.X + d.Y - int(math.Ceil(float64(d.X)/2))
} else {
return d.X + d.Y - int(math.Floor(float64(d.X)/2))
}
}
}
func scanInput() Input {
var input Input
fmt.Scan(&input.ShipCount)
fmt.Scan(&input.EntityCount)
for i := 0; i < input.EntityCount; i++ {
var e Entity
fmt.Scan(&e.ID, &e.Type, &e.X, &e.Y, &e.Arg1, &e.Arg2, &e.Arg3, &e.Arg4)
input.Entities = append(input.Entities, e)
switch e.Type {
case BARREL:
input.Barrels = append(input.Barrels, Barrel(e))
case CANNONBALL:
input.Cannonballs = append(input.Cannonballs, Cannonball(e))
case MINE:
input.Mines = append(input.Mines, Mine(e))
case SHIP:
switch e.Arg4 {
case 1:
input.Ships = append(input.Ships, Ship(e))
case 0:
input.Enemies = append(input.Enemies, Ship(e))
}
}
}
return input
}
package main
import (
"bytes"
"image"
"testing"
)
func TestTurn(t *testing.T) {
w := &bytes.Buffer{}
g := &Game{Writer: w}
g.Turn(Input{
Ships: []Ship{
{},
},
Barrels: []Barrel{
{Arg1: 15, Point: image.Pt(10, 5)},
},
})
if got, want := w.String(), "MOVE 10 5\n"; got != want {
t.Fatalf("w.String() = %q, want %q", got, want)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment