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"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
"golang.org/x/net/websocket"
|
||||
|
||||
"hackerbots.us/server"
|
||||
"bitbucket.org/hackerbots/server"
|
||||
"code.google.com/p/go.net/websocket"
|
||||
)
|
||||
|
||||
func connect(addr string) (*websocket.Conn, error) {
|
||||
func connect(server string, port int) (*websocket.Conn, error) {
|
||||
origin := "http://localhost/"
|
||||
url := fmt.Sprintf("%s/ws/", addr)
|
||||
url := fmt.Sprintf("ws://%s:%d/ws/", server, port)
|
||||
return websocket.Dial(url, "", origin)
|
||||
}
|
||||
|
||||
// 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.
|
||||
type Client struct {
|
||||
ForceJSON bool
|
||||
GameId string
|
||||
Name string
|
||||
Port int
|
||||
Server string
|
||||
StatsReq server.StatsRequest
|
||||
Verbose bool
|
||||
Player Player
|
||||
Game server.GameParam
|
||||
|
||||
boardstate *server.Boardstate
|
||||
boardstate server.Boardstate
|
||||
enc encoder
|
||||
dec decoder
|
||||
ws *websocket.Conn
|
||||
|
||||
// visualization members
|
||||
width, height float64
|
||||
viewX, viewY int
|
||||
StateStream chan *server.Boardstate
|
||||
Die chan struct{}
|
||||
|
||||
Player
|
||||
}
|
||||
|
||||
type encoder interface {
|
||||
@ -49,9 +45,12 @@ type decoder interface {
|
||||
Decode(v interface{}) error
|
||||
}
|
||||
|
||||
// Negotiate runs through the hackerbots negociation protocol.
|
||||
func (c *Client) Negotiate(clientType string, player Player) (err error) {
|
||||
c.ws, err = connect(c.Server)
|
||||
// Negociate runs through the hackerbots negociation protocol.
|
||||
func (c *Client) Negociate() (err error) {
|
||||
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 {
|
||||
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" {
|
||||
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 {
|
||||
Type string `json:"type"`
|
||||
@ -81,7 +83,7 @@ func (c *Client) Negotiate(clientType string, player Player) (err error) {
|
||||
}{
|
||||
Name: c.Name,
|
||||
Useragent: "gobot",
|
||||
Type: clientType,
|
||||
Type: "robot",
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
@ -100,6 +102,9 @@ func (c *Client) Negotiate(clientType string, player Player) (err error) {
|
||||
if c.Game.Type != "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" {
|
||||
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)
|
||||
}
|
||||
|
||||
switch clientType {
|
||||
case "robot":
|
||||
conf := server.ClientConfig{
|
||||
ID: c.GameId,
|
||||
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":
|
||||
conf := server.ClientConfig{
|
||||
ID: c.GameId,
|
||||
Stats: map[string]server.StatsRequest{
|
||||
c.Name: c.StatsReq,
|
||||
},
|
||||
}
|
||||
|
||||
c.width = c.Game.BoardSize.Width
|
||||
c.height = c.Game.BoardSize.Height
|
||||
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)
|
||||
}
|
||||
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
|
||||
}
|
||||
@ -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
|
||||
// sends the server the Player's instruction.
|
||||
func (c *Client) Play() error {
|
||||
bs := &server.Boardstate{}
|
||||
log.Printf("%s: starting loop", c.Name)
|
||||
|
||||
var err error
|
||||
for {
|
||||
err = c.dec.Decode(bs)
|
||||
err = c.dec.Decode(&c.boardstate)
|
||||
if err != nil {
|
||||
return errors.New(fmt.Sprintf("%s: Connection likely lost: %s", c.Name, err))
|
||||
}
|
||||
|
||||
select {
|
||||
case c.StateStream <- bs:
|
||||
default:
|
||||
instructions := make(map[string]server.Instruction)
|
||||
|
||||
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 {
|
||||
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
|
||||
|
||||
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 behavior.
|
||||
@ -8,31 +17,171 @@ import "hackerbots.us/server"
|
||||
// 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.
|
||||
type Player interface {
|
||||
|
||||
// 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
|
||||
Update(bot *server.Robot, bs *server.Boardstate) server.Instruction
|
||||
}
|
||||
|
||||
type Spectator struct{}
|
||||
|
||||
func (s Spectator) SetIDs(map[string]string) {}
|
||||
|
||||
func (s Spectator) GetStats() map[string]server.StatsRequest {
|
||||
return nil
|
||||
// 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
|
||||
}
|
||||
|
||||
func (s Spectator) Update(bs *server.Boardstate) map[string]server.Instruction {
|
||||
return nil
|
||||
// NewSimplePlayer simply returns a populated, usable *SimplePlayer
|
||||
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