Last active
January 31, 2020 20:11
-
-
Save peterhellberg/e1709cb9ee9e87f32ae901faa8e8c323 to your computer and use it in GitHub Desktop.
Coders of the Caribbean in Go
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
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 | |
} |
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
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