Compare commits
2 Commits
master
...
robot-upgr
Author | SHA1 | Date | |
---|---|---|---|
|
f8ecde3e81 | ||
|
a2436fe8d8 |
128
client.go
128
client.go
@ -5,40 +5,36 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"log"
|
||||||
|
|
||||||
"golang.org/x/net/websocket"
|
"bitbucket.org/hackerbots/server"
|
||||||
|
"code.google.com/p/go.net/websocket"
|
||||||
"hackerbots.us/server"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func connect(addr string) (*websocket.Conn, error) {
|
func connect(server string, port int) (*websocket.Conn, error) {
|
||||||
origin := "http://localhost/"
|
origin := "http://localhost/"
|
||||||
url := fmt.Sprintf("%s/ws/", addr)
|
url := fmt.Sprintf("ws://%s:%d/ws/", server, port)
|
||||||
return websocket.Dial(url, "", origin)
|
return websocket.Dial(url, "", origin)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Client keeps track of connection to server and has two interesting methods:
|
// Client keeps track of connection to server and has two interesting methods:
|
||||||
// Negotiate and Play. Users of this struct will likely use most everything as
|
// Negociate and Play. Users of this struct will likely use most everything as
|
||||||
// is while defining their own Player to specify desired game play behavior.
|
// is while defining their own Player to specify desired game play behavior.
|
||||||
type Client struct {
|
type Client struct {
|
||||||
ForceJSON bool
|
ForceJSON bool
|
||||||
GameId string
|
GameId string
|
||||||
Name string
|
Name string
|
||||||
|
Port int
|
||||||
Server string
|
Server string
|
||||||
|
StatsReq server.StatsRequest
|
||||||
|
Verbose bool
|
||||||
|
Player Player
|
||||||
Game server.GameParam
|
Game server.GameParam
|
||||||
|
|
||||||
boardstate *server.Boardstate
|
boardstate server.Boardstate
|
||||||
enc encoder
|
enc encoder
|
||||||
dec decoder
|
dec decoder
|
||||||
ws *websocket.Conn
|
ws *websocket.Conn
|
||||||
|
|
||||||
// visualization members
|
|
||||||
width, height float64
|
|
||||||
viewX, viewY int
|
|
||||||
StateStream chan *server.Boardstate
|
|
||||||
Die chan struct{}
|
|
||||||
|
|
||||||
Player
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type encoder interface {
|
type encoder interface {
|
||||||
@ -49,9 +45,12 @@ type decoder interface {
|
|||||||
Decode(v interface{}) error
|
Decode(v interface{}) error
|
||||||
}
|
}
|
||||||
|
|
||||||
// Negotiate runs through the hackerbots negociation protocol.
|
// Negociate runs through the hackerbots negociation protocol.
|
||||||
func (c *Client) Negotiate(clientType string, player Player) (err error) {
|
func (c *Client) Negociate() (err error) {
|
||||||
c.ws, err = connect(c.Server)
|
if c.Verbose {
|
||||||
|
log.Printf("%s: trying to connect to game '%s'", c.Name, c.GameId)
|
||||||
|
}
|
||||||
|
c.ws, err = connect(c.Server, c.Port)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.New(fmt.Sprintf("connection failure: %s", err))
|
return errors.New(fmt.Sprintf("connection failure: %s", err))
|
||||||
}
|
}
|
||||||
@ -73,6 +72,9 @@ func (c *Client) Negotiate(clientType string, player Player) (err error) {
|
|||||||
if err != nil || idreq.Type == "failure" {
|
if err != nil || idreq.Type == "failure" {
|
||||||
return errors.New(fmt.Sprintf("failure: %+v", idreq))
|
return errors.New(fmt.Sprintf("failure: %+v", idreq))
|
||||||
}
|
}
|
||||||
|
if c.Verbose {
|
||||||
|
log.Printf("%s: idreq: %+v", c.Name, idreq)
|
||||||
|
}
|
||||||
|
|
||||||
err = websocket.JSON.Send(c.ws, struct {
|
err = websocket.JSON.Send(c.ws, struct {
|
||||||
Type string `json:"type"`
|
Type string `json:"type"`
|
||||||
@ -81,7 +83,7 @@ func (c *Client) Negotiate(clientType string, player Player) (err error) {
|
|||||||
}{
|
}{
|
||||||
Name: c.Name,
|
Name: c.Name,
|
||||||
Useragent: "gobot",
|
Useragent: "gobot",
|
||||||
Type: clientType,
|
Type: "robot",
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -100,6 +102,9 @@ func (c *Client) Negotiate(clientType string, player Player) (err error) {
|
|||||||
if c.Game.Type != "gameparam" {
|
if c.Game.Type != "gameparam" {
|
||||||
return errors.New("didn't receive a good gameparam")
|
return errors.New("didn't receive a good gameparam")
|
||||||
}
|
}
|
||||||
|
if c.Verbose {
|
||||||
|
log.Printf("%s: game parameters: %+v", c.Name, c.Game)
|
||||||
|
}
|
||||||
|
|
||||||
if c.Game.Encoding == "json" {
|
if c.Game.Encoding == "json" {
|
||||||
c.enc = json.NewEncoder(c.ws)
|
c.enc = json.NewEncoder(c.ws)
|
||||||
@ -109,48 +114,39 @@ func (c *Client) Negotiate(clientType string, player Player) (err error) {
|
|||||||
c.dec = gob.NewDecoder(c.ws)
|
c.dec = gob.NewDecoder(c.ws)
|
||||||
}
|
}
|
||||||
|
|
||||||
switch clientType {
|
conf := server.ClientConfig{
|
||||||
case "robot":
|
ID: c.GameId,
|
||||||
conf := server.ClientConfig{
|
Stats: map[string]server.StatsRequest{
|
||||||
ID: c.GameId,
|
c.Name: c.StatsReq,
|
||||||
Stats: player.GetStats(),
|
},
|
||||||
}
|
|
||||||
|
|
||||||
err = websocket.JSON.Send(c.ws, conf)
|
|
||||||
|
|
||||||
var handshake struct {
|
|
||||||
Id string `json:"id"`
|
|
||||||
Success bool `json:"success"`
|
|
||||||
Type string `json:"type"`
|
|
||||||
server.Failure
|
|
||||||
}
|
|
||||||
websocket.JSON.Receive(c.ws, &handshake)
|
|
||||||
if !handshake.Success {
|
|
||||||
return errors.New(handshake.Reason)
|
|
||||||
}
|
|
||||||
|
|
||||||
// we don't do anything useful with dstats, but could be interesting to
|
|
||||||
// pass along to the player?
|
|
||||||
dstats := struct {
|
|
||||||
Stats map[string]server.Stats `json:"stats"`
|
|
||||||
Type string `json:"type"`
|
|
||||||
}{}
|
|
||||||
|
|
||||||
err = websocket.JSON.Receive(c.ws, &dstats)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
ids := make(map[string]string)
|
|
||||||
for name, entry := range dstats.Stats {
|
|
||||||
ids[entry.Id] = name
|
|
||||||
}
|
|
||||||
player.SetIDs(ids)
|
|
||||||
|
|
||||||
case "spectator":
|
|
||||||
}
|
}
|
||||||
|
|
||||||
c.width = c.Game.BoardSize.Width
|
err = websocket.JSON.Send(c.ws, conf)
|
||||||
c.height = c.Game.BoardSize.Height
|
|
||||||
|
var handshake struct {
|
||||||
|
Id string `json:"id"`
|
||||||
|
Success bool `json:"success"`
|
||||||
|
Type string `json:"type"`
|
||||||
|
server.Failure
|
||||||
|
}
|
||||||
|
websocket.JSON.Receive(c.ws, &handshake)
|
||||||
|
if !handshake.Success {
|
||||||
|
return errors.New(handshake.Reason)
|
||||||
|
}
|
||||||
|
if c.Verbose {
|
||||||
|
log.Printf("%s: handshake: %+v", c.Name, handshake)
|
||||||
|
}
|
||||||
|
|
||||||
|
// we don't do anything useful with dstats, but could be interesting to
|
||||||
|
// pass along to the player?
|
||||||
|
dstats := struct {
|
||||||
|
Stats map[string]server.Stats `json:"stats"`
|
||||||
|
Type string `json:"type"`
|
||||||
|
}{}
|
||||||
|
err = websocket.JSON.Receive(c.ws, &dstats)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -159,20 +155,22 @@ func (c *Client) Negotiate(clientType string, player Player) (err error) {
|
|||||||
// server, and passes this along to the embedded Player. Following this it
|
// server, and passes this along to the embedded Player. Following this it
|
||||||
// sends the server the Player's instruction.
|
// sends the server the Player's instruction.
|
||||||
func (c *Client) Play() error {
|
func (c *Client) Play() error {
|
||||||
bs := &server.Boardstate{}
|
log.Printf("%s: starting loop", c.Name)
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
for {
|
for {
|
||||||
err = c.dec.Decode(bs)
|
err = c.dec.Decode(&c.boardstate)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.New(fmt.Sprintf("%s: Connection likely lost: %s", c.Name, err))
|
return errors.New(fmt.Sprintf("%s: Connection likely lost: %s", c.Name, err))
|
||||||
}
|
}
|
||||||
|
|
||||||
select {
|
instructions := make(map[string]server.Instruction)
|
||||||
case c.StateStream <- bs:
|
|
||||||
default:
|
for _,bot := range(c.boardstate.MyRobots){
|
||||||
|
instructions[bot.Id] = c.Player.Update(&bot, &c.boardstate)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = c.enc.Encode(c.Update(bs))
|
err = c.enc.Encode(instructions)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -1,49 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"flag"
|
|
||||||
"fmt"
|
|
||||||
"math/rand"
|
|
||||||
"os"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"hackerbots.us/client"
|
|
||||||
"hackerbots.us/server"
|
|
||||||
)
|
|
||||||
|
|
||||||
var addr = flag.String("addr", "ws://localhost:8666", "server hostname")
|
|
||||||
var forceJSON = flag.Bool("json", false, "force json encoding")
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
rand.Seed(time.Now().UnixNano())
|
|
||||||
var gameId string
|
|
||||||
flag.Parse()
|
|
||||||
if flag.NArg() < 1 {
|
|
||||||
gameId = "debug"
|
|
||||||
} else {
|
|
||||||
gameId = flag.Arg(0)
|
|
||||||
}
|
|
||||||
|
|
||||||
c := &client.Client{
|
|
||||||
Server: *addr,
|
|
||||||
Name: "bspect",
|
|
||||||
GameId: gameId,
|
|
||||||
ForceJSON: *forceJSON,
|
|
||||||
StateStream: make(chan *server.Boardstate),
|
|
||||||
Die: make(chan struct{}),
|
|
||||||
Player: client.Spectator{},
|
|
||||||
}
|
|
||||||
var err error
|
|
||||||
err = c.Negotiate("spectator", c.Player)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Fprintf(os.Stderr, "%s: failed to negotiate: %s\n", c.Name, err)
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
if err := c.Play(); err != nil {
|
|
||||||
close(c.Die)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
c.Visualize()
|
|
||||||
}
|
|
@ -1,85 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"flag"
|
|
||||||
"fmt"
|
|
||||||
"math/rand"
|
|
||||||
"os"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"hackerbots.us/client"
|
|
||||||
"hackerbots.us/server"
|
|
||||||
)
|
|
||||||
|
|
||||||
var hp = flag.Int("hp", 50, "")
|
|
||||||
var speed = flag.Int("speed", 50, "")
|
|
||||||
var acceleration = flag.Int("acceleration", 50, "")
|
|
||||||
var scannerRadius = flag.Int("srad", 50, "scanner radius")
|
|
||||||
var turnSpeed = flag.Int("omega", 50, "turn speed")
|
|
||||||
var fireRate = flag.Int("fire-rate", 50, "scanner radius")
|
|
||||||
var weaponRadius = flag.Int("wrad", 50, "weapon radius")
|
|
||||||
var weaponDamage = flag.Int("wdamage", 50, "weapons umph")
|
|
||||||
var weaponSpeed = flag.Int("wspeed", 50, "weapons speed")
|
|
||||||
|
|
||||||
// XXX: add TurnSpeed, WeaponDamage, WeaponSpeed
|
|
||||||
|
|
||||||
var addr = flag.String("addr", "ws://localhost:8666", "server hostname")
|
|
||||||
var botname = flag.String("name", "gobot", "the name that other players will see")
|
|
||||||
var forceJSON = flag.Bool("json", false, "force json encoding")
|
|
||||||
var botType = flag.String("bot", "simple", "which Bot")
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
rand.Seed(time.Now().UnixNano())
|
|
||||||
var gameId string
|
|
||||||
flag.Parse()
|
|
||||||
if flag.NArg() < 1 {
|
|
||||||
gameId = "debug"
|
|
||||||
} else {
|
|
||||||
gameId = flag.Arg(0)
|
|
||||||
}
|
|
||||||
|
|
||||||
c := &client.Client{
|
|
||||||
Server: *addr,
|
|
||||||
Name: *botname,
|
|
||||||
GameId: gameId,
|
|
||||||
// XXX: update with missing fields
|
|
||||||
ForceJSON: *forceJSON,
|
|
||||||
StateStream: make(chan *server.Boardstate),
|
|
||||||
Die: make(chan struct{}),
|
|
||||||
}
|
|
||||||
sr := server.StatsRequest{
|
|
||||||
Hp: *hp,
|
|
||||||
Speed: *speed,
|
|
||||||
Acceleration: *acceleration,
|
|
||||||
ScannerRadius: *scannerRadius,
|
|
||||||
TurnSpeed: *turnSpeed,
|
|
||||||
FireRate: *fireRate,
|
|
||||||
WeaponRadius: *weaponRadius,
|
|
||||||
WeaponDamage: *weaponDamage,
|
|
||||||
WeaponSpeed: *weaponSpeed,
|
|
||||||
}
|
|
||||||
|
|
||||||
switch *botType {
|
|
||||||
case "simple":
|
|
||||||
c.Player = client.NewSimplePlayer(800, 600, sr)
|
|
||||||
case "fraserbot":
|
|
||||||
c.Player = client.NewFraserbot("Fraserbot")
|
|
||||||
default:
|
|
||||||
fmt.Fprintf(os.Stderr, "must specify a known bot \n")
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
var err error
|
|
||||||
err = c.Negotiate("robot", c.Player)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Fprintf(os.Stderr, "%s: failed to negociate: %s\n", c.Name, err)
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
if err := c.Play(); err != nil {
|
|
||||||
close(c.Die)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
c.Visualize()
|
|
||||||
}
|
|
134
fraserbot.go
134
fraserbot.go
@ -1,134 +0,0 @@
|
|||||||
package client
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"log"
|
|
||||||
"math/rand"
|
|
||||||
|
|
||||||
"hackerbots.us/server"
|
|
||||||
"hackerbots.us/vector"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Fraserbot is a bad ass motherfucker, that will fuck SHIT UUUUUP
|
|
||||||
type Fraserbot struct {
|
|
||||||
knownObstacles map[string]server.Obstacle
|
|
||||||
nearestEnemy *server.OtherRobot
|
|
||||||
fireat *vector.Point2d
|
|
||||||
moveto *vector.Point2d
|
|
||||||
name string
|
|
||||||
botIDs map[string]string
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewFraserbot simply returns a populated, usable *Fraserbot
|
|
||||||
func NewFraserbot(name string) *Fraserbot {
|
|
||||||
return &Fraserbot{
|
|
||||||
knownObstacles: make(map[string]server.Obstacle),
|
|
||||||
name: name,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetStats returns a map with an entry for each robot the player will control
|
|
||||||
// containing the desired stats for that robot
|
|
||||||
func (p *Fraserbot) GetStats() map[string]server.StatsRequest {
|
|
||||||
s := make(map[string]server.StatsRequest)
|
|
||||||
|
|
||||||
s[fmt.Sprintf("%v_MAIN", p.name)] = server.StatsRequest{
|
|
||||||
Hp: 100,
|
|
||||||
Speed: 10,
|
|
||||||
Acceleration: 10,
|
|
||||||
ScannerRadius: 10,
|
|
||||||
TurnSpeed: 10,
|
|
||||||
FireRate: 30,
|
|
||||||
WeaponRadius: 20,
|
|
||||||
WeaponDamage: 30,
|
|
||||||
WeaponSpeed: 30,
|
|
||||||
}
|
|
||||||
|
|
||||||
s[fmt.Sprintf("%v_Jr", p.name)] = server.StatsRequest{
|
|
||||||
Hp: 10,
|
|
||||||
Speed: 100,
|
|
||||||
Acceleration: 10,
|
|
||||||
ScannerRadius: 60,
|
|
||||||
TurnSpeed: 48,
|
|
||||||
FireRate: 1,
|
|
||||||
WeaponRadius: 10,
|
|
||||||
WeaponDamage: 10,
|
|
||||||
WeaponSpeed: 1,
|
|
||||||
}
|
|
||||||
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetIDs provides the mapping of names to ID's for each bot
|
|
||||||
func (p *Fraserbot) SetIDs(ids map[string]string) {
|
|
||||||
p.botIDs = ids
|
|
||||||
log.Println(ids)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update is our implementation of recieving and processing a server.Boardstate
|
|
||||||
// from the server
|
|
||||||
func (p *Fraserbot) Update(bs *server.Boardstate) map[string]server.Instruction {
|
|
||||||
instructions := make(map[string]server.Instruction)
|
|
||||||
for _, bot := range bs.MyRobots {
|
|
||||||
me := bot
|
|
||||||
|
|
||||||
// We're just starting out
|
|
||||||
if p.moveto == nil {
|
|
||||||
p.moveto = &me.Position
|
|
||||||
}
|
|
||||||
|
|
||||||
speed := float64(200)
|
|
||||||
// If we're close to where we want to go then pick a new
|
|
||||||
// place to go, we done good
|
|
||||||
if me.Position.Sub(*p.moveto).Mag() < 30 {
|
|
||||||
p.moveto = p.randomDirection(me.Position, 400)
|
|
||||||
}
|
|
||||||
|
|
||||||
if me.ProbeResult != nil && me.ProbeResult.Type == "obstacle" {
|
|
||||||
speed = -50
|
|
||||||
p.moveto = p.randomDirection(me.Position, 600)
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(me.Scanners) > 0 {
|
|
||||||
// Find the bots that are not mine
|
|
||||||
|
|
||||||
var index int
|
|
||||||
found := false
|
|
||||||
for i, entry := range me.Scanners {
|
|
||||||
_, ok := p.botIDs[entry.Id]
|
|
||||||
if !ok {
|
|
||||||
index = i
|
|
||||||
found = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if found {
|
|
||||||
// log.Println(me.Scanners[0])
|
|
||||||
for _, bot := range bs.OtherRobots {
|
|
||||||
if bot.Id == me.Scanners[index].Id {
|
|
||||||
p.fireat = &bot.Position
|
|
||||||
log.Printf("%v Found Enemy: %v\n", me.Name, bot.Name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
instructions[bot.Id] = server.Instruction{
|
|
||||||
MoveTo: p.moveto,
|
|
||||||
TargetSpeed: &speed,
|
|
||||||
FireAt: p.fireat,
|
|
||||||
Probe: p.moveto,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return instructions
|
|
||||||
}
|
|
||||||
|
|
||||||
// randomDirection is a spot within 200 of the current position
|
|
||||||
func (p *Fraserbot) randomDirection(pos vector.Point2d, dist float64) *vector.Point2d {
|
|
||||||
pt := vector.Vector2d{
|
|
||||||
X: (rand.Float64() * dist) - (dist / 2) + pos.X,
|
|
||||||
Y: (rand.Float64() * dist) - (dist / 2) + pos.Y,
|
|
||||||
}.ToPoint()
|
|
||||||
return &pt
|
|
||||||
}
|
|
78
gobot/main.go
Normal file
78
gobot/main.go
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"flag"
|
||||||
|
"log"
|
||||||
|
"math/rand"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"bitbucket.org/hackerbots/client"
|
||||||
|
"bitbucket.org/hackerbots/server"
|
||||||
|
)
|
||||||
|
|
||||||
|
var hp = flag.Int("hp", 50, "")
|
||||||
|
var speed = flag.Int("speed", 50, "")
|
||||||
|
var acceleration = flag.Int("acceleration", 50, "")
|
||||||
|
var scannerRadius = flag.Int("srad", 50, "scanner radius")
|
||||||
|
var turnSpeed = flag.Int("omega", 50, "turn speed")
|
||||||
|
var fireRate = flag.Int("fire-rate", 50, "scanner radius")
|
||||||
|
var weaponRadius = flag.Int("wrad", 50, "weapon radius")
|
||||||
|
var weaponDamage = flag.Int("wdamage", 50, "weapons umph")
|
||||||
|
var weaponSpeed = flag.Int("wspeed", 50, "weapons speed")
|
||||||
|
|
||||||
|
// XXX: add TurnSpeed, WeaponDamage, WeaponSpeed
|
||||||
|
|
||||||
|
var serverHostname = flag.String("server", "localhost", "server hostname")
|
||||||
|
var port = flag.Int("port", 8666, "server port")
|
||||||
|
var botname = flag.String("name", "gobot", "the name that other players will see")
|
||||||
|
var verbose = flag.Bool("verbose", false, "run verbosly")
|
||||||
|
var forceJSON = flag.Bool("json", false, "force json encoding")
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
log.SetFlags(log.Ldate | log.Ltime | log.Lshortfile)
|
||||||
|
|
||||||
|
rand.Seed(time.Now().UnixNano())
|
||||||
|
var gameId string
|
||||||
|
flag.Parse()
|
||||||
|
if flag.NArg() < 1 {
|
||||||
|
gameId = "debug"
|
||||||
|
} else {
|
||||||
|
gameId = flag.Arg(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
client.Verbose = *verbose
|
||||||
|
c := &client.Client{
|
||||||
|
Server: *serverHostname,
|
||||||
|
Port: *port,
|
||||||
|
Name: *botname,
|
||||||
|
GameId: gameId,
|
||||||
|
// XXX: update with missing fields
|
||||||
|
StatsReq: server.StatsRequest{
|
||||||
|
Hp: *hp,
|
||||||
|
Speed: *speed,
|
||||||
|
Acceleration: *acceleration,
|
||||||
|
ScannerRadius: *scannerRadius,
|
||||||
|
TurnSpeed: *turnSpeed,
|
||||||
|
FireRate: *fireRate,
|
||||||
|
WeaponRadius: *weaponRadius,
|
||||||
|
WeaponDamage: *weaponDamage,
|
||||||
|
WeaponSpeed: *weaponSpeed,
|
||||||
|
},
|
||||||
|
Verbose: *verbose,
|
||||||
|
ForceJSON: *forceJSON,
|
||||||
|
}
|
||||||
|
var err error
|
||||||
|
err = c.Negociate()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("%s: failed to negociate: %s", c.Name, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
c.Player = client.NewSimplePlayer(
|
||||||
|
c.Game.BoardSize.Width,
|
||||||
|
c.Game.BoardSize.Height,
|
||||||
|
)
|
||||||
|
|
||||||
|
if err := c.Play(); err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
27
license
27
license
@ -1,27 +0,0 @@
|
|||||||
Copyright (c) 2016, Fraser Graham, Stephen McQuay
|
|
||||||
|
|
||||||
All rights reserved.
|
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without modification,
|
|
||||||
are permitted provided that the following conditions are met:
|
|
||||||
|
|
||||||
* Redistributions of source code must retain the above copyright notice,
|
|
||||||
this list of conditions and the following disclaimer.
|
|
||||||
* Redistributions in binary form must reproduce the above copyright notice,
|
|
||||||
this list of conditions and the following disclaimer in the documentation
|
|
||||||
and/or other materials provided with the distribution.
|
|
||||||
* Neither the name of server nor the names of its contributors
|
|
||||||
may be used to endorse or promote products derived from this software
|
|
||||||
without specific prior written permission.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
||||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
||||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
||||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
|
||||||
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
|
||||||
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
|
||||||
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
|
||||||
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
|
||||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
|
||||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
||||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
197
player.go
197
player.go
@ -1,6 +1,15 @@
|
|||||||
package client
|
package client
|
||||||
|
|
||||||
import "hackerbots.us/server"
|
import (
|
||||||
|
"fmt"
|
||||||
|
"math"
|
||||||
|
"math/rand"
|
||||||
|
|
||||||
|
"bitbucket.org/hackerbots/server"
|
||||||
|
"bitbucket.org/hackerbots/vector"
|
||||||
|
)
|
||||||
|
|
||||||
|
var Verbose bool = false
|
||||||
|
|
||||||
// Player is the interface that is implemented when specifying non-default
|
// Player is the interface that is implemented when specifying non-default
|
||||||
// player behavior.
|
// player behavior.
|
||||||
@ -8,31 +17,171 @@ import "hackerbots.us/server"
|
|||||||
// The general case will be to implement a Player type that contains the magic
|
// The general case will be to implement a Player type that contains the magic
|
||||||
// required to slay other robots quickly while staying alive for a long time.
|
// required to slay other robots quickly while staying alive for a long time.
|
||||||
type Player interface {
|
type Player interface {
|
||||||
|
Update(bot *server.Robot, bs *server.Boardstate) server.Instruction
|
||||||
// GetStats returns a map with an entry for each robot the player will control
|
|
||||||
// containing the desired stats for that robot
|
|
||||||
GetStats() map[string]server.StatsRequest
|
|
||||||
|
|
||||||
// SetIDs is called from the client once the server
|
|
||||||
// has accepted the robots supplied in GetStats and validated
|
|
||||||
// their config, the data passed into SetIDs is a mapping of
|
|
||||||
// bot name to server side bot ID that is used in all bot
|
|
||||||
// dats sent from the server
|
|
||||||
SetIDs(map[string]string)
|
|
||||||
|
|
||||||
// Update is called on reciept of a board state packet and the response is
|
|
||||||
// the instructions for each robot in a map of robot id to instructions
|
|
||||||
Update(bs *server.Boardstate) map[string]server.Instruction
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type Spectator struct{}
|
// SimplePlayer is our default player and stands as a starting point for your
|
||||||
|
// own Player implementations.
|
||||||
func (s Spectator) SetIDs(map[string]string) {}
|
type SimplePlayer struct {
|
||||||
|
me server.Robot
|
||||||
func (s Spectator) GetStats() map[string]server.StatsRequest {
|
width, height float64
|
||||||
return nil
|
knownObstacles map[string]server.Obstacle
|
||||||
|
nearestEnemy *server.OtherRobot
|
||||||
|
fireat *vector.Point2d
|
||||||
|
moveto *vector.Point2d
|
||||||
|
speed float64
|
||||||
|
maxSpeed float64
|
||||||
|
safeDistance float64
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s Spectator) Update(bs *server.Boardstate) map[string]server.Instruction {
|
// NewSimplePlayer simply returns a populated, usable *SimplePlayer
|
||||||
return nil
|
func NewSimplePlayer(width, height float64) *SimplePlayer {
|
||||||
|
return &SimplePlayer{
|
||||||
|
knownObstacles: make(map[string]server.Obstacle),
|
||||||
|
width: width,
|
||||||
|
height: height,
|
||||||
|
maxSpeed: 1000,
|
||||||
|
safeDistance: 50,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Recv is our implementation of receiving a server.Boardstate from the server
|
||||||
|
func (p *SimplePlayer) Update(bot *server.Robot, bs *server.Boardstate) server.Instruction{
|
||||||
|
p.me = *bot
|
||||||
|
p.speed = 1000
|
||||||
|
if p.me.Health <= 0{
|
||||||
|
return server.Instruction{}
|
||||||
|
}
|
||||||
|
|
||||||
|
p.recon(bs)
|
||||||
|
p.navigate()
|
||||||
|
|
||||||
|
probe_point := p.me.Position.Add(p.me.Heading.Scale(p.safeDistance))
|
||||||
|
|
||||||
|
if Verbose {
|
||||||
|
fmt.Printf("PROBE SENT: %v\n",probe_point)
|
||||||
|
}
|
||||||
|
|
||||||
|
return server.Instruction{
|
||||||
|
MoveTo: p.moveto,
|
||||||
|
TargetSpeed: &p.speed,
|
||||||
|
FireAt: p.fireat,
|
||||||
|
Probe: &probe_point,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *SimplePlayer) navigate() {
|
||||||
|
if Verbose {
|
||||||
|
fmt.Printf("%v S:%v H:%v TS:%v\n\tX:%v Y:%v\n\tHX:%v HY:%v\n",
|
||||||
|
p.me.Name, p.me.Speed, p.me.Health, p.me.TargetSpeed,
|
||||||
|
p.me.Position.X, p.me.Position.Y,
|
||||||
|
p.me.Heading.X, p.me.Heading.Y)
|
||||||
|
|
||||||
|
if p.me.MoveTo != nil {
|
||||||
|
fmt.Printf("\tTX:%v TY:%v\n",
|
||||||
|
p.me.MoveTo.X, p.me.MoveTo.Y)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// if !p.probe(p.me.Position.Add(p.me.Heading.Scale(p.safeDistance))) {
|
||||||
|
// if !p.probe(*p.moveto) {
|
||||||
|
// p.moveto = p.randomDirectionDrift(p.moveto, 20)
|
||||||
|
// // p.speed = p.maxSpeed
|
||||||
|
// fmt.Printf("Obstacle?\n")
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
if p.me.ProbeResult != nil {
|
||||||
|
p.moveto = p.randomDirectionDrift(&p.me.Position, 100)
|
||||||
|
p.speed = -20
|
||||||
|
if Verbose {
|
||||||
|
fmt.Printf("Probe %v\n", p.me.ProbeResult)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if p.me.Collision != nil {
|
||||||
|
p.moveto = p.randomDirectionDrift(&p.me.Position, 100)
|
||||||
|
p.speed = -20
|
||||||
|
if Verbose {
|
||||||
|
fmt.Printf("Hit!\n")
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if p.moveto == nil {
|
||||||
|
p.moveto = p.randomDirection()
|
||||||
|
// p.speed = p.maxSpeed
|
||||||
|
if Verbose {
|
||||||
|
fmt.Printf("Start\n")
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
togo := p.me.Position.Sub(*p.moveto).Mag()
|
||||||
|
if togo < p.safeDistance+5 {
|
||||||
|
p.moveto = p.randomDirection()
|
||||||
|
// p.speed = p.maxSpeed
|
||||||
|
if Verbose {
|
||||||
|
fmt.Printf("New Dest\n")
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *SimplePlayer) recon(bs *server.Boardstate) {
|
||||||
|
|
||||||
|
// simplest shooting strategy ... need to do the following:
|
||||||
|
// not shoot through buildings
|
||||||
|
// shoot at where the robot will be, not where it was.
|
||||||
|
p.nearestEnemy = nil
|
||||||
|
p.fireat = nil
|
||||||
|
closest := math.Inf(1)
|
||||||
|
for _, enemy := range bs.OtherRobots {
|
||||||
|
dist := p.me.Position.Sub(enemy.Position).Mag()
|
||||||
|
if dist < closest && dist > p.safeDistance {
|
||||||
|
p.nearestEnemy = &enemy
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if p.nearestEnemy != nil {
|
||||||
|
point := p.nearestEnemy.Position.Add(p.nearestEnemy.Heading.Scale(p.safeDistance))
|
||||||
|
p.fireat = &point
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *SimplePlayer) randomDirectionDrift(start *vector.Point2d, drift float64) *vector.Point2d {
|
||||||
|
for {
|
||||||
|
pt := vector.Vector2d{
|
||||||
|
X: start.X - drift + rand.Float64() * drift * 2,
|
||||||
|
Y: start.Y - drift + rand.Float64() * drift * 2,
|
||||||
|
}.ToPoint()
|
||||||
|
|
||||||
|
if pt.X > 0 && pt.X < p.width && pt.Y > 0 && pt.Y < p.height {
|
||||||
|
return &pt
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *SimplePlayer) randomDirection() *vector.Point2d {
|
||||||
|
pt := vector.Vector2d{
|
||||||
|
X: rand.Float64() * p.width,
|
||||||
|
Y: rand.Float64() * p.height,
|
||||||
|
}.ToPoint()
|
||||||
|
return &pt
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *SimplePlayer) probe(destination vector.Point2d) bool {
|
||||||
|
// XXX: make test for this
|
||||||
|
for _, v := range p.knownObstacles {
|
||||||
|
collided, _, _ := vector.RectIntersection(
|
||||||
|
v.Bounds,
|
||||||
|
p.me.Position,
|
||||||
|
destination.Sub(p.me.Position),
|
||||||
|
)
|
||||||
|
if collided {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
|
153
simple_player.go
153
simple_player.go
@ -1,153 +0,0 @@
|
|||||||
package client
|
|
||||||
|
|
||||||
import (
|
|
||||||
"math"
|
|
||||||
"math/rand"
|
|
||||||
|
|
||||||
"hackerbots.us/server"
|
|
||||||
"hackerbots.us/vector"
|
|
||||||
)
|
|
||||||
|
|
||||||
// SimplePlayer is our default player and stands as a starting point for your
|
|
||||||
// own Player implementations.
|
|
||||||
type SimplePlayer struct {
|
|
||||||
me server.Robot
|
|
||||||
width, height float64
|
|
||||||
knownObstacles map[string]server.Obstacle
|
|
||||||
nearestEnemy *server.OtherRobot
|
|
||||||
fireat *vector.Point2d
|
|
||||||
moveto *vector.Point2d
|
|
||||||
speed float64
|
|
||||||
maxSpeed float64
|
|
||||||
safeDistance float64
|
|
||||||
stats server.StatsRequest
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewSimplePlayer simply returns a populated, usable *SimplePlayer
|
|
||||||
func NewSimplePlayer(width, height float64, stats server.StatsRequest) *SimplePlayer {
|
|
||||||
return &SimplePlayer{
|
|
||||||
knownObstacles: make(map[string]server.Obstacle),
|
|
||||||
width: width,
|
|
||||||
height: height,
|
|
||||||
maxSpeed: 100,
|
|
||||||
safeDistance: 40,
|
|
||||||
stats: stats,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *SimplePlayer) SetIDs(map[string]string) {}
|
|
||||||
|
|
||||||
// GetStats returns a map with an entry for each robot the player will control
|
|
||||||
// containing the desired stats for that robot
|
|
||||||
func (p *SimplePlayer) GetStats() map[string]server.StatsRequest {
|
|
||||||
s := make(map[string]server.StatsRequest)
|
|
||||||
s["simple"] = p.stats
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update is our implementation of recieving and processing a server.Boardstate
|
|
||||||
// from the server
|
|
||||||
func (p *SimplePlayer) Update(bs *server.Boardstate) map[string]server.Instruction {
|
|
||||||
instructions := make(map[string]server.Instruction)
|
|
||||||
|
|
||||||
for _, bot := range bs.MyRobots {
|
|
||||||
p.me = bot
|
|
||||||
p.speed = 1000
|
|
||||||
if p.me.Health <= 0 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
p.recon(bs)
|
|
||||||
p.navigate()
|
|
||||||
|
|
||||||
probe_point := p.me.Position.Add(p.me.Heading.Scale(p.safeDistance))
|
|
||||||
|
|
||||||
instructions[bot.Id] = server.Instruction{
|
|
||||||
MoveTo: p.moveto,
|
|
||||||
TargetSpeed: &p.speed,
|
|
||||||
FireAt: p.fireat,
|
|
||||||
Probe: &probe_point,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return instructions
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *SimplePlayer) navigate() {
|
|
||||||
if p.moveto == nil {
|
|
||||||
p.moveto = p.randomDirection()
|
|
||||||
}
|
|
||||||
|
|
||||||
togo := p.me.Position.Sub(*p.moveto).Mag()
|
|
||||||
if togo < p.safeDistance+5 {
|
|
||||||
p.moveto = p.randomDirection()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if !p.probe(p.me.Position.Add(p.me.Heading.Scale(p.safeDistance))) {
|
|
||||||
p.speed = 0
|
|
||||||
if !p.probe(*p.moveto) {
|
|
||||||
p.moveto = p.randomDirection()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if p.me.Collision != nil {
|
|
||||||
p.moveto = p.randomDirection()
|
|
||||||
p.speed = 0
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *SimplePlayer) recon(bs *server.Boardstate) {
|
|
||||||
// XXX: need to keep track of seen objects ..
|
|
||||||
|
|
||||||
// simplest shooting strategy ... need to do the following:
|
|
||||||
// not shoot through buildings
|
|
||||||
// shoot at where the robot will be, not where it was.
|
|
||||||
p.nearestEnemy = nil
|
|
||||||
p.fireat = nil
|
|
||||||
closest := math.Inf(1)
|
|
||||||
for _, enemy := range bs.OtherRobots {
|
|
||||||
dist := p.me.Position.Sub(enemy.Position).Mag()
|
|
||||||
if dist < closest && dist > p.safeDistance {
|
|
||||||
p.nearestEnemy = &enemy
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if p.nearestEnemy != nil {
|
|
||||||
point := p.nearestEnemy.Position.Add(p.nearestEnemy.Heading.Scale(p.safeDistance))
|
|
||||||
p.fireat = &point
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Instruction is our default implementation of preparing a map of information
|
|
||||||
// to be sent to server.
|
|
||||||
func (p *SimplePlayer) Instruction() map[string]server.Instruction {
|
|
||||||
return map[string]server.Instruction{
|
|
||||||
p.me.Id: {
|
|
||||||
MoveTo: p.moveto,
|
|
||||||
TargetSpeed: &p.speed,
|
|
||||||
FireAt: p.fireat,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *SimplePlayer) randomDirection() *vector.Point2d {
|
|
||||||
pt := vector.Vector2d{
|
|
||||||
X: rand.Float64() * p.width,
|
|
||||||
Y: rand.Float64() * p.height,
|
|
||||||
}.ToPoint()
|
|
||||||
return &pt
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *SimplePlayer) probe(destination vector.Point2d) bool {
|
|
||||||
// XXX: make test for this
|
|
||||||
for _, v := range p.knownObstacles {
|
|
||||||
collided, _, _ := vector.RectIntersection(
|
|
||||||
v.Bounds,
|
|
||||||
p.me.Position,
|
|
||||||
destination.Sub(p.me.Position),
|
|
||||||
)
|
|
||||||
if collided {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
192
spectator.go
192
spectator.go
@ -1,192 +0,0 @@
|
|||||||
package client
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"math"
|
|
||||||
|
|
||||||
"github.com/nsf/termbox-go"
|
|
||||||
|
|
||||||
"hackerbots.us/server"
|
|
||||||
)
|
|
||||||
|
|
||||||
var botDown rune = 'v'
|
|
||||||
var botUp rune = '^'
|
|
||||||
var botRight rune = '>'
|
|
||||||
var botLeft rune = '<'
|
|
||||||
|
|
||||||
type size struct {
|
|
||||||
width, height int
|
|
||||||
}
|
|
||||||
|
|
||||||
// Recv is our implementation of receiving a server.Boardstate from the server
|
|
||||||
func (s *Client) Recv(bs *server.Boardstate) map[string]server.Instruction {
|
|
||||||
s.StateStream <- bs
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Client) Visualize() {
|
|
||||||
err := termbox.Init()
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
s.viewX, s.viewY = termbox.Size()
|
|
||||||
|
|
||||||
events := make(chan termbox.Event, 1024)
|
|
||||||
go func() {
|
|
||||||
for {
|
|
||||||
events <- termbox.PollEvent()
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
termbox.HideCursor()
|
|
||||||
termbox.Clear(termbox.ColorBlack, termbox.ColorBlack)
|
|
||||||
|
|
||||||
func() {
|
|
||||||
for {
|
|
||||||
select {
|
|
||||||
case event := <-events:
|
|
||||||
switch event.Type {
|
|
||||||
case termbox.EventKey:
|
|
||||||
switch event.Key {
|
|
||||||
case termbox.KeyCtrlZ, termbox.KeyCtrlC:
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
switch event.Ch {
|
|
||||||
case 'q':
|
|
||||||
return
|
|
||||||
case 'c':
|
|
||||||
termbox.Clear(termbox.ColorBlack, termbox.ColorBlack)
|
|
||||||
}
|
|
||||||
|
|
||||||
case termbox.EventResize:
|
|
||||||
s.viewX, s.viewY = event.Width, event.Height
|
|
||||||
case termbox.EventError:
|
|
||||||
panic(fmt.Sprintf("Quitting because of termbox error:\n%v\n", event.Err))
|
|
||||||
}
|
|
||||||
case u := <-s.StateStream:
|
|
||||||
termbox.Clear(termbox.ColorBlack, termbox.ColorBlack)
|
|
||||||
|
|
||||||
for _, obstacle := range u.Obstacles {
|
|
||||||
startX := int((obstacle.Bounds.A.X / s.width) * float64(s.viewX))
|
|
||||||
stopX := int((obstacle.Bounds.B.X / s.width) * float64(s.viewX))
|
|
||||||
startY := int((obstacle.Bounds.A.Y / s.height) * float64(s.viewY))
|
|
||||||
stopY := int((obstacle.Bounds.B.Y / s.height) * float64(s.viewY))
|
|
||||||
for x := startX; x < stopX; x++ {
|
|
||||||
for y := startY; y < stopY; y++ {
|
|
||||||
termbox.SetCell(
|
|
||||||
x,
|
|
||||||
s.viewY-y,
|
|
||||||
' ',
|
|
||||||
termbox.ColorBlack, termbox.ColorBlue,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, bot := range u.OtherRobots {
|
|
||||||
x := int((bot.Position.X / s.width) * float64(s.viewX))
|
|
||||||
y := int((bot.Position.Y / s.height) * float64(s.viewY))
|
|
||||||
var b rune
|
|
||||||
if math.Abs(bot.Heading.X) > math.Abs(bot.Heading.Y) {
|
|
||||||
if bot.Heading.X > 0 {
|
|
||||||
b = botRight
|
|
||||||
} else {
|
|
||||||
b = botLeft
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if bot.Heading.Y > 0 {
|
|
||||||
b = botUp
|
|
||||||
} else {
|
|
||||||
b = botDown
|
|
||||||
}
|
|
||||||
}
|
|
||||||
c := termbox.ColorRed
|
|
||||||
if bot.Health <= 0 {
|
|
||||||
c = termbox.ColorBlack
|
|
||||||
}
|
|
||||||
termbox.SetCell(
|
|
||||||
x,
|
|
||||||
s.viewY-y,
|
|
||||||
b,
|
|
||||||
c, termbox.ColorBlack,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, p := range u.Projectiles {
|
|
||||||
x := int((p.Position.X / s.width) * float64(s.viewX))
|
|
||||||
y := int((p.Position.Y / s.height) * float64(s.viewY))
|
|
||||||
termbox.SetCell(
|
|
||||||
x,
|
|
||||||
s.viewY-y,
|
|
||||||
'·',
|
|
||||||
termbox.ColorWhite|termbox.AttrBold, termbox.ColorBlack,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, splosion := range u.Splosions {
|
|
||||||
startX := int(((splosion.Position.X - float64(splosion.Radius)) / s.width) * float64(s.viewX))
|
|
||||||
startY := int(((splosion.Position.Y - float64(splosion.Radius)) / s.height) * float64(s.viewY))
|
|
||||||
stopX := int(((splosion.Position.X+float64(splosion.Radius))/s.width)*float64(s.viewX)) + 1
|
|
||||||
stopY := int(((splosion.Position.Y+float64(splosion.Radius))/s.height)*float64(s.viewY)) + 1
|
|
||||||
for x := startX; x < stopX; x++ {
|
|
||||||
for y := startY; y < stopY; y++ {
|
|
||||||
realX := float64(x) * s.width / float64(s.viewX)
|
|
||||||
realY := float64(y) * s.height / float64(s.viewY)
|
|
||||||
dX := realX - splosion.Position.X
|
|
||||||
dY := realY - splosion.Position.Y
|
|
||||||
curRad := math.Sqrt(dX*dX + dY*dY)
|
|
||||||
if curRad < float64(splosion.Radius) {
|
|
||||||
termbox.SetCell(
|
|
||||||
x,
|
|
||||||
s.viewY-y,
|
|
||||||
'·',
|
|
||||||
termbox.ColorYellow|termbox.AttrBold, termbox.ColorRed,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, bot := range u.MyRobots {
|
|
||||||
x := int((bot.Position.X / s.width) * float64(s.viewX))
|
|
||||||
y := int((bot.Position.Y / s.height) * float64(s.viewY))
|
|
||||||
var b rune
|
|
||||||
if math.Abs(bot.Heading.X) > math.Abs(bot.Heading.Y) {
|
|
||||||
if bot.Heading.X > 0 {
|
|
||||||
b = botRight
|
|
||||||
} else {
|
|
||||||
b = botLeft
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if bot.Heading.Y > 0 {
|
|
||||||
b = botUp
|
|
||||||
} else {
|
|
||||||
b = botDown
|
|
||||||
}
|
|
||||||
}
|
|
||||||
c := termbox.ColorWhite
|
|
||||||
if bot.Health <= 0 {
|
|
||||||
c = termbox.ColorBlack
|
|
||||||
}
|
|
||||||
termbox.SetCell(
|
|
||||||
x,
|
|
||||||
s.viewY-y,
|
|
||||||
b,
|
|
||||||
c|termbox.AttrBold, termbox.ColorBlack,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
err := termbox.Flush()
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
case <-s.Die:
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
termbox.Close()
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user