From 0c254caff5010c256b9ad160d0edc0f922053ebe Mon Sep 17 00:00:00 2001 From: Stephen McQuay Date: Sat, 29 Mar 2014 00:12:01 -0700 Subject: [PATCH] Split out binary from library I still want to do a few things: - move common hackerbot code to a package called hackerbot - make the stuff in here more trivially usable outside of this repo --- botserv.go | 173 --------------------------------------- main.go => gobot/main.go | 23 +++--- robot.go | 148 ++++++++++++++++----------------- shims.go | 33 ++++++++ 4 files changed, 119 insertions(+), 258 deletions(-) delete mode 100644 botserv.go rename main.go => gobot/main.go (82%) create mode 100644 shims.go diff --git a/botserv.go b/botserv.go deleted file mode 100644 index 0dd3053..0000000 --- a/botserv.go +++ /dev/null @@ -1,173 +0,0 @@ -package main - -import ( - "fmt" - - "bitbucket.org/hackerbots/vector" -) - -type ClientConfig struct { - ID string `json:"id"` - Stats map[string]StatsRequest `json:"stats"` -} - -type BoardSize struct { - Width float32 `json:"width"` - Height float32 `json:"height"` -} - -// This is kind of misused here; we set Name, but expect to get the other -// values back from the server -type GameParam struct { - BoardSize BoardSize `json:"boardsize"` - MaxPoints int `json:"max_points"` - Name string `json:"name"` - Type string `json:"type"` - Encoding string `json:"encoding"` -} - -type StatsRequest struct { - Hp int `json:"hp"` - Speed int `json:"speed"` - Acceleration int `json:"acceleration"` - ScannerRadius int `json:"scanner_radius"` - TurnSpeed int `json:"turn_speed"` - FireRate int `json:"fire_rate"` - WeaponRadius int `json:"weapon_radius"` - WeaponDamage int `json:"weapon_damage"` - WeaponSpeed int `json:"weapon_speed"` -} - -type Scanner struct { - Position govector.Point2d `json:"position"` - Stats StatsRequest `json:"stats"` -} - -type Robot struct { - Id string `json:"id"` - Name string `json:"name"` - Message string `json:"-"` - Stats Stats `json:"-"` - TargetSpeed float32 `json:"-"` - Speed float32 `json:"speed"` - Health int `json:"health"` - RepairCounter float32 `json:"repair"` - ScanCounter float32 `json:"scan_bonus"` - ActiveScan bool `json:"-"` - Position govector.Point2d `json:"position"` - Heading govector.Vector2d `json:"heading"` - MoveTo *govector.Point2d `json:"-"` - FireAt *govector.Point2d `json:"-"` - Scanners []Scanner `json:"scanners"` - LastFired int `json:"-"` - Collision *Collision `json:"collision"` - Hit bool `json:"hit"` - Probe *govector.Point2d `json:"probe"` - ProbeResult *Collision `json:"probe_result"` -} - -type Collision struct { - govector.Point2d - Type string `json:"type"` -} - -type Stats struct { - Hp int `json:"hp"` - Speed float32 `json:"speed"` - Acceleration float32 `json:"acceleration"` - WeaponRadius int `json:"weapon_radius"` - ScannerRadius int `json:"scanner_radius"` - TurnSpeed int `json:"turn_speed"` - FireRate int `json:"fire_rate"` - WeaponDamage int `json:"weapon_damage"` - WeaponSpeed float32 `json:"weapon_speed"` -} - -type Projectile struct { - Id string `json:"id"` - Position govector.Point2d `json:"position"` - MoveTo govector.Point2d `json:"move_to"` - Radius int `json:"radius"` - Speed float32 `json:"speed"` - Damage int `json:"damage"` -} - -type Splosion struct { - Id string `json:"id"` - Position govector.Point2d `json:"position"` - Radius int `json:"radius"` - MaxDamage int `json:"damage"` - MinDamage int `json:"damage"` - Lifespan int `json:"lifespan"` -} - -type Instruction struct { - Message *string `json:"message,omitempty"` - MoveTo *govector.Point2d `json:"move_to,omitempty"` - FireAt *govector.Point2d `json:"fire_at,omitempty"` - Probe *govector.Point2d `json:"probe,omitempty"` - TargetSpeed *float32 `json:"target_speed,omitempty"` - Repair *bool `json:"repair,omitempty"` - Scan *bool `json:"scan,omitempty"` -} - -type Boardstate struct { - MyRobots []Robot `json:"my_robots"` - OtherRobots []OtherRobot `json:"robots"` - Projectiles []Projectile `json:"projectiles"` - Splosions []Splosion `json:"splosions"` - Obstacles []MiniObstacle `json:"objects"` - Reset bool `json:"reset"` - Type string `json:"type"` - Turn int `json:"turn"` - AllBots []BotHealth `json:"all_bots"` - Messages []string `json:"messages"` -} - -type OtherRobot struct { - Id string `json:"id"` - Name string `json:"name"` - Position govector.Point2d `json:"position"` - Heading govector.Vector2d `json:"heading"` - Health int `json:"health"` -} - -type MiniObstacle [4]int - -func (mo *MiniObstacle) id() string { - return fmt.Sprintf( - "%x%x%x%x", - mo[0], - mo[1], - mo[2], - mo[3], - ) -} - -func (mo MiniObstacle) String() string { - return mo.id() -} - -func (mo *MiniObstacle) toObstacle() Obstacle { - return Obstacle{ - Bounds: govector.AABB2d{ - A: govector.Point2d{float32(mo[0]), float32(mo[1])}, - B: govector.Point2d{float32(mo[2]), float32(mo[3])}, - }, - } -} - -type Obstacle struct { - Bounds govector.AABB2d `json:"bounds"` - Hp int `json:"-"` -} - -type BotHealth struct { - RobotId string `json:"robot_id"` - Health int `json:"health"` -} - -type Failure struct { - Reason string `json:"reason"` - Type string `json:"type"` -} diff --git a/main.go b/gobot/main.go similarity index 82% rename from main.go rename to gobot/main.go index 5a04e76..fcd1679 100644 --- a/main.go +++ b/gobot/main.go @@ -7,6 +7,9 @@ import ( "math/rand" "sync" "time" + + "bitbucket.org/hackerbots/botclient" + "bitbucket.org/hackerbots/botserv" ) var hp = flag.Int("hp", 50, "") @@ -44,15 +47,13 @@ func main() { var wg sync.WaitGroup for i := 0; i < *botcount; i++ { - r := &robot{ - server: *server, - port: *port, - name: fmt.Sprintf("%s%d", *botname, i), - game: GameParam{ - Name: gameName, - }, + r := &botclient.Robot{ + Server: *server, + Port: *port, + Name: fmt.Sprintf("%s%d", *botname, i), + GameName: gameName, // XXX: update with missing fields - statsReq: StatsRequest{ + StatsReq: botserv.StatsRequest{ Hp: *hp, Speed: *speed, Acceleration: *acceleration, @@ -63,11 +64,11 @@ func main() { WeaponDamage: *weaponDamage, WeaponSpeed: *weaponSpeed, }, - wg: &wg, - knownObstacles: make(map[string]Obstacle), + Wg: &wg, + KnownObstacles: make(map[string]botserv.Obstacle), } wg.Add(1) - go r.play() + go r.Play() } wg.Wait() } diff --git a/robot.go b/robot.go index 61c98dc..6e153ae 100644 --- a/robot.go +++ b/robot.go @@ -1,4 +1,4 @@ -package main +package botclient import ( "encoding/gob" @@ -10,6 +10,7 @@ import ( "math/rand" "sync" + "bitbucket.org/hackerbots/botserv" "bitbucket.org/hackerbots/vector" "code.google.com/p/go.net/websocket" ) @@ -24,27 +25,30 @@ func connect(server string, port int) (*websocket.Conn, error) { return websocket.Dial(url, "", origin) } -type robot struct { - server string - port int +type Robot struct { + verbose bool + forceJSON bool + Server string + Port int ws *websocket.Conn - game GameParam + game botserv.GameParam playerId string - name string - statsReq StatsRequest - statsCalculated Stats + Name string + GameName string + StatsReq botserv.StatsRequest + statsCalculated botserv.Stats speed float32 moveto *govector.Point2d fireat *govector.Point2d - boardstate Boardstate - me Robot - knownObstacles map[string]Obstacle - nearestEnemy *OtherRobot + boardstate botserv.Boardstate + me botserv.Robot + KnownObstacles map[string]botserv.Obstacle + nearestEnemy *botserv.OtherRobot // TODO: don't know if I like how this is done ... I would rather send // a signal over a chanel - wg *sync.WaitGroup + Wg *sync.WaitGroup } type encoder interface { @@ -55,11 +59,11 @@ type decoder interface { Decode(v interface{}) error } -func (r *robot) negociate() (err error) { - if *verbose { - log.Printf("%s: trying to connect to game '%s'", r.name, r.game.Name) +func (r *Robot) negociate() (err error) { + if r.verbose { + log.Printf("%s: trying to connect to game '%s'", r.Name, r.GameName) } - r.ws, err = connect(r.server, r.port) + r.ws, err = connect(r.Server, r.Port) if err != nil { return errors.New(fmt.Sprintf("connection failure: %s", err)) } @@ -67,7 +71,7 @@ func (r *robot) negociate() (err error) { err = websocket.JSON.Send(r.ws, struct { Id string `json:"id"` }{ - r.game.Name, + r.GameName, }) if err != nil { return err @@ -81,8 +85,8 @@ func (r *robot) negociate() (err error) { if err != nil || idreq.Type == "failure" { return errors.New(fmt.Sprintf("failure: %+v", idreq)) } - if *verbose { - log.Printf("%s: idreq: %+v", r.name, idreq) + if r.verbose { + log.Printf("%s: idreq: %+v", r.Name, idreq) } err = websocket.JSON.Send(r.ws, struct { @@ -90,7 +94,7 @@ func (r *robot) negociate() (err error) { Name string `json:"name"` Useragent string `json:"useragent"` }{ - Name: r.name, + Name: r.Name, Useragent: "gobot", Type: "robot", }) @@ -99,7 +103,7 @@ func (r *robot) negociate() (err error) { } supportedEncs := []string{"bson", "json", "gob"} - if *forceJSON { + if r.forceJSON { supportedEncs = []string{"json"} } err = websocket.JSON.Send(r.ws, supportedEncs) @@ -111,14 +115,14 @@ func (r *robot) negociate() (err error) { if r.game.Type != "gameparam" { return errors.New("didn't receive a good gameparam") } - if *verbose { - log.Printf("%s: game parameters: %+v", r.name, r.game) + if r.verbose { + log.Printf("%s: game parameters: %+v", r.Name, r.game) } - conf := ClientConfig{ - ID: r.game.Name, - Stats: map[string]StatsRequest{ - r.name: r.statsReq, + conf := botserv.ClientConfig{ + ID: r.GameName, + Stats: map[string]botserv.StatsRequest{ + r.Name: r.StatsReq, }, } @@ -128,48 +132,48 @@ func (r *robot) negociate() (err error) { Id string `json:"id"` Success bool `json:"success"` Type string `json:"type"` - Failure + botserv.Failure } websocket.JSON.Receive(r.ws, &handshake) if !handshake.Success { return errors.New(handshake.Reason) } r.playerId = handshake.Id - if *verbose { - log.Printf("%s: handshake: %+v", r.name, handshake) + if r.verbose { + log.Printf("%s: handshake: %+v", r.Name, handshake) } dstats := struct { - Stats map[string]Stats `json:"stats"` - Type string `json:"type"` + Stats map[string]botserv.Stats `json:"stats"` + Type string `json:"type"` }{} err = websocket.JSON.Receive(r.ws, &dstats) if err != nil { return err } - log.Printf("%s: recv stats", r.name) + log.Printf("%s: recv stats", r.Name) // this player only ever has one robot, so we're just picking off our own // stats - _, ok := dstats.Stats[r.name] + _, ok := dstats.Stats[r.Name] if !ok { return errors.New("my name not found in stats map") } - r.statsCalculated = dstats.Stats[r.name] + r.statsCalculated = dstats.Stats[r.Name] return nil } -func (r *robot) play() { - defer r.wg.Done() +func (r *Robot) Play() { + defer r.Wg.Done() var err error err = r.negociate() if err != nil { - log.Printf("%s: failed to negociate: %s", r.name, err) + log.Printf("%s: failed to negociate: %s", r.Name, err) return } - log.Printf("%s: starting loop", r.name) + log.Printf("%s: starting loop", r.Name) var enc encoder var dec decoder @@ -184,19 +188,19 @@ func (r *robot) play() { for { r.speed = float32(maxSpeed) - if *verbose { - log.Printf("%s: about to wait on boardstate", r.name) + if r.verbose { + log.Printf("%s: about to wait on boardstate", r.Name) } err = dec.Decode(&r.boardstate) if err != nil { - log.Printf("%s: Connection lost", r.name) + log.Printf("%s: Connection lost", r.Name) return } - if *verbose { - log.Printf("%s: one recv boardstate", r.name) + if r.verbose { + log.Printf("%s: one recv boardstate", r.Name) } // TODO: I need a truly-verbose flag? - // if *verbose { + // if r.verbose { // log.Printf("\n\n%#v\n\n", r.boardstate) // } @@ -208,15 +212,15 @@ func (r *robot) play() { continue } - if *verbose { - log.Printf("%s before: %+v", r.name, r.moveto) + if r.verbose { + log.Printf("%s before: %+v", r.Name, r.moveto) } r.navigate() - if *verbose { - log.Printf("%s after: %+v", r.name, r.moveto) + if r.verbose { + log.Printf("%s after: %+v", r.Name, r.moveto) } - instruction := map[string]Instruction{ + instruction := map[string]botserv.Instruction{ r.me.Id: { MoveTo: r.moveto, TargetSpeed: &r.speed, @@ -231,10 +235,11 @@ func (r *robot) play() { } } -func (r *robot) recon() { - for _, o := range r.boardstate.Obstacles { - if _, ok := r.knownObstacles[o.id()]; !ok { - r.knownObstacles[o.id()] = o.toObstacle() +func (r *Robot) recon() { + for _, o := range r.boardstate.Objects { + obj := MiniObstacle(o) + if _, ok := r.KnownObstacles[obj.id()]; !ok { + r.KnownObstacles[obj.id()] = obj.toObstacle() } } @@ -256,7 +261,7 @@ func (r *robot) recon() { } } -func (r *robot) randomDirection() *govector.Point2d { +func (r *Robot) randomDirection() *govector.Point2d { p := govector.Vector2d{ X: rand.Float32() * r.game.BoardSize.Width, Y: rand.Float32() * r.game.BoardSize.Height, @@ -264,9 +269,9 @@ func (r *robot) randomDirection() *govector.Point2d { return &p } -func (r *robot) probe(destination govector.Point2d) bool { +func (r *Robot) probe(destination govector.Point2d) bool { // XXX: make test for this - for _, v := range r.knownObstacles { + for _, v := range r.KnownObstacles { collided, _, _ := govector.RectIntersection( v.Bounds, r.me.Position, @@ -279,36 +284,31 @@ func (r *robot) probe(destination govector.Point2d) bool { return true } -func (r *robot) navigate() { - if r.boardstate.Reset { - // XXX We actually *want* to get here, but never do .. - panic("reset") - r.moveto = r.randomDirection() - } +func (r *Robot) navigate() { if r.moveto == nil { - if *verbose { - log.Printf("%s: nil", r.name) + if r.verbose { + log.Printf("%s: nil", r.Name) } r.moveto = r.randomDirection() } togo := r.me.Position.Sub(*r.moveto).Mag() if togo < safeDistance+5 { - if *verbose { - log.Printf("%s got to destination", r.name) + if r.verbose { + log.Printf("%s got to destination", r.Name) } r.moveto = r.randomDirection() return } if !r.probe(r.me.Position.Add(r.me.Heading.Scale(safeDistance))) { - if *verbose { - log.Printf("%s going to run into something", r.name) + if r.verbose { + log.Printf("%s going to run into something", r.Name) } r.speed = 0 if !r.probe(*r.moveto) { - if *verbose { - log.Printf("%s unsafe to move, choose new direction", r.name) + if r.verbose { + log.Printf("%s unsafe to move, choose new direction", r.Name) } r.moveto = r.randomDirection() return @@ -316,8 +316,8 @@ func (r *robot) navigate() { } if r.me.Collision != nil { // XXX: I am being told I am here ... - if *verbose { - log.Printf("%s apparent collision: %#v", r.name, r.me.Collision) + if r.verbose { + log.Printf("%s apparent collision: %#v", r.Name, r.me.Collision) } r.moveto = r.randomDirection() r.speed = 0 diff --git a/shims.go b/shims.go new file mode 100644 index 0000000..090add8 --- /dev/null +++ b/shims.go @@ -0,0 +1,33 @@ +package botclient + +import ( + "fmt" + + "bitbucket.org/hackerbots/botserv" + "bitbucket.org/hackerbots/vector" +) + +type MiniObstacle [4]int + +func (mo *MiniObstacle) id() string { + return fmt.Sprintf( + "%x%x%x%x", + mo[0], + mo[1], + mo[2], + mo[3], + ) +} + +func (mo MiniObstacle) String() string { + return mo.id() +} + +func (mo *MiniObstacle) toObstacle() botserv.Obstacle { + return botserv.Obstacle{ + Bounds: govector.AABB2d{ + A: govector.Point2d{float32(mo[0]), float32(mo[1])}, + B: govector.Point2d{float32(mo[2]), float32(mo[3])}, + }, + } +}