From e33f2c0c4f9f607eb3e985f686baa043389376c5 Mon Sep 17 00:00:00 2001 From: Stephen McQuay Date: Fri, 27 Sep 2013 22:27:05 -0700 Subject: [PATCH] moved code out of awkward bots repo --- .gitignore | 1 + game.go | 54 ++++++++++++++++++++++------ http.go | 15 ++++---- player.go | 15 ++++---- protocol.go | 99 ++++++++++++++++++++++++++++++++++++++++++++++------ robot.go | 84 ++++++++++++++++++++++++++++++++++++++++++++ spectator.go | 3 +- 7 files changed, 233 insertions(+), 38 deletions(-) create mode 100644 robot.go diff --git a/.gitignore b/.gitignore index 639977f..e76cdaf 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ botserv *.swp +tags diff --git a/game.go b/game.go index 1eb83dd..aa25d95 100644 --- a/game.go +++ b/game.go @@ -1,7 +1,6 @@ package main import ( - "bitbucket.org/hackerbots/bot" v "bitbucket.org/hackerbots/vector" "log" "sort" @@ -10,11 +9,46 @@ import ( const maxPlayer = 128 +type Config struct { + ID string `json:"id"` + Name string `json:"name"` + // TODO: candidate for embedding? + Stats Stats `json:"stats"` +} + +type Boardstate struct { + Robots []Robot `json:"robots"` + Projectiles []Projectile `json:"projectiles"` + Splosions []Splosion `json:"splosions"` + Reset bool `json:"reset"` + Type string `json:"type"` + Turn int `json:"turn"` +} + +func (bs *Boardstate) EmptySlices() { + bs.Robots = bs.Robots[:0] + bs.Projectiles = bs.Projectiles[:0] + bs.Splosions = bs.Splosions[:0] +} + +func NewBoardstate() *Boardstate { + return &Boardstate{ + Robots: []Robot{}, + Projectiles: []Projectile{}, + Type: "boardstate", + } +} + +type Scanner struct { + Position v.Point2d `json:"position"` + Stats Stats `json:"stats"` +} + type game struct { id string players map[*player]bool - projectiles map[*bot.Projectile]bool - splosions map[*bot.Splosion]bool + projectiles map[*Projectile]bool + splosions map[*Splosion]bool register chan *player unregister chan *player turn int @@ -30,8 +64,8 @@ func NewGame(id string, width, height float64) *game { id: id, register: make(chan *player), unregister: make(chan *player, maxPlayer), - projectiles: make(map[*bot.Projectile]bool), - splosions: make(map[*bot.Splosion]bool), + projectiles: make(map[*Projectile]bool), + splosions: make(map[*Splosion]bool), players: make(map[*player]bool), turn: 0, width: width, @@ -46,7 +80,7 @@ func NewGame(id string, width, height float64) *game { func (g *game) run() { var t0, t1 time.Time - payload := bot.NewBoardstate() + payload := NewBoardstate() for { select { case <-g.kill: @@ -102,7 +136,7 @@ func (g *game) run() { } payload.Robots = append(payload.Robots, p.Robot) } - sort.Sort(bot.RobotSorter{Robots: payload.Robots}) + sort.Sort(RobotSorter{Robots: payload.Robots}) payload.Projectiles = append(payload.Projectiles, g.nudgeProjectiles()..., @@ -150,8 +184,8 @@ func (g *game) run() { } } -func (g *game) nudgeProjectiles() (rprojectiles []bot.Projectile) { - rprojectiles = make([]bot.Projectile, 0) +func (g *game) nudgeProjectiles() (rprojectiles []Projectile) { + rprojectiles = make([]Projectile, 0) for p := range g.projectiles { newPos := v.Move(p.Position, p.MoveTo, float64(p.Speed), delta) @@ -170,7 +204,7 @@ func (g *game) nudgeProjectiles() (rprojectiles []bot.Projectile) { delete(g.projectiles, p) // Spawn a splosion - splo := &bot.Splosion{ + splo := &Splosion{ Id: p.Id, Position: p.Position, Radius: p.Radius, diff --git a/http.go b/http.go index 626daac..3a43e5b 100644 --- a/http.go +++ b/http.go @@ -1,7 +1,6 @@ package main import ( - "bitbucket.org/hackerbots/bot" "code.google.com/p/go.net/websocket" "encoding/json" "fmt" @@ -81,7 +80,7 @@ func listGames(w http.ResponseWriter, req *http.Request) { func addPlayer(ws *websocket.Conn) { // route to appropriate game ... - var gid bot.GameID + var gid GameID err := websocket.JSON.Receive(ws, &gid) if err != nil { log.Println("problem parsing the requested game id") @@ -94,7 +93,7 @@ func addPlayer(ws *websocket.Conn) { if !ok { log.Println("ERROR: game not found") - websocket.JSON.Send(ws, bot.NewFailure("game 404")) + websocket.JSON.Send(ws, NewFailure("game 404")) return } @@ -102,18 +101,18 @@ func addPlayer(ws *websocket.Conn) { conf, err := Negociate(ws, id, game.width, game.height) if err != nil { - websocket.JSON.Send(ws, bot.NewFailure(err.Error())) + websocket.JSON.Send(ws, NewFailure(err.Error())) } if conf != nil { p := &player{ - Robot: bot.Robot{ + Robot: Robot{ Stats: conf.Stats, Id: id, Name: conf.Name, Health: conf.Stats.Hp, - Scanners: make([]bot.Scanner, 0)}, - send: make(chan *bot.Boardstate), + Scanners: make([]Scanner, 0)}, + send: make(chan *Boardstate), ws: ws, } p.reset() @@ -126,7 +125,7 @@ func addPlayer(ws *websocket.Conn) { log.Printf("game %s: player %v has been disconnected from this game\n", gid.Id, p.Robot.Id) } else { s := &Spectator{ - send: make(chan *bot.Boardstate), + send: make(chan *Boardstate), ws: ws, } game.sregister <- s diff --git a/player.go b/player.go index 146d1e2..d97e32a 100644 --- a/player.go +++ b/player.go @@ -1,7 +1,6 @@ package main import ( - "bitbucket.org/hackerbots/bot" v "bitbucket.org/hackerbots/vector" "code.google.com/p/go.net/websocket" "log" @@ -11,9 +10,9 @@ import ( type player struct { ws *websocket.Conn - Robot bot.Robot - send chan *bot.Boardstate - Instruction bot.Instruction + Robot Robot + send chan *Boardstate + Instruction Instruction } func (p *player) sender() { @@ -31,7 +30,7 @@ func (p *player) recv() { for { // XXX: need to mark myself as having received something, also binding // such action to a particular game turn ID - var msg bot.Instruction + var msg Instruction err := websocket.JSON.Receive(p.ws, &msg) if err != nil { // TODO: perhaps we could be a bit more precise in the handling of @@ -136,7 +135,7 @@ func (p *player) scan(players map[*player]bool) { } dist := v.Distance(player.Robot.Position, p.Robot.Position) if dist < float64(p.Robot.Stats.ScannerRadius) { - s := bot.Scanner{ + s := Scanner{ Position: v.Point2d{ X: player.Robot.Position.X, Y: player.Robot.Position.Y, @@ -147,7 +146,7 @@ func (p *player) scan(players map[*player]bool) { } } -func (p *player) fire(projectiles map[*bot.Projectile]bool) *bot.Projectile { +func (p *player) fire(projectiles map[*Projectile]bool) *Projectile { // XXX: is this to prevent us from having multiple projectiles from the // same bot? for proj := range projectiles { @@ -156,7 +155,7 @@ func (p *player) fire(projectiles map[*bot.Projectile]bool) *bot.Projectile { } } - return &bot.Projectile{ + return &Projectile{ Id: p.Robot.Id, Position: p.Robot.Position, MoveTo: *p.Robot.FireAt, diff --git a/protocol.go b/protocol.go index 97c9f66..4d78ad7 100644 --- a/protocol.go +++ b/protocol.go @@ -1,21 +1,100 @@ package main import ( - "bitbucket.org/hackerbots/bot" "code.google.com/p/go.net/websocket" "errors" "log" ) -func Negociate(ws *websocket.Conn, id string, width, height float64) (*bot.Config, error) { +type GameID struct { + Id string `json:"id"` +} + +// > identify +type IdRequest struct { + Type string `json:"type"` + AssignedID string `json:"id"` + Failure +} + +func NewIdRequest(id string) *IdRequest { + return &IdRequest{ + Type: "idreq", + AssignedID: id, + } +} + +// < [robot | spectator], name, client-type, game ID +type ClientID struct { + Type string `json:"type"` + Name string `json:"name"` + Useragent string `json:"useragent"` +} + +func (c *ClientID) Valid() (bool, string) { + switch c.Type { + case "robot", "spectator": + return true, "" + } + return false, "usergent must be 'robot' or 'spectator'" +} + +type BoardSize struct { + Width float64 `json:"width"` + Height float64 `json:"height"` +} + +type GameParam struct { + BoardSize BoardSize `json:"boardsize"` + Type string `json:"type"` +} + +// > [OK | FULL | NOT AUTH], board size, game params +func NewGameParam(w, h float64) *GameParam { + return &GameParam{ + BoardSize: BoardSize{ + Width: w, + Height: h, + }, + Type: "gameparam", + } +} + +type Handshake struct { + ID string `json:"id"` + Success bool `json:"success"` + Type string `json:"type"` +} + +func NewHandshake(id string, success bool) *Handshake { + return &Handshake{ + ID: id, + Success: success, + Type: "handshake", + } +} + +type Failure struct { + Reason string `json:"reason"` + Type string `json:"type"` +} + +func NewFailure(reason string) *Failure { + return &Failure{ + Reason: reason, + Type: "failure", + } +} + +func Negociate(ws *websocket.Conn, id string, width, height float64) (*Config, error) { var err error - err = websocket.JSON.Send(ws, bot.NewIdRequest(id)) + err = websocket.JSON.Send(ws, NewIdRequest(id)) if err != nil { return nil, errors.New("generic server error") } - var clientid bot.ClientID + var clientid ClientID err = websocket.JSON.Receive(ws, &clientid) if err != nil { return nil, errors.New("could not parse id") @@ -24,22 +103,22 @@ func Negociate(ws *websocket.Conn, id string, width, height float64) (*bot.Confi log.Printf("clientid is invalid: %+v", clientid) websocket.JSON.Send( ws, - bot.NewFailure(msg), + NewFailure(msg), ) return nil, errors.New(msg) } log.Printf("clientid: %+v", clientid) - gameParam := bot.NewGameParam(width, height) + gameParam := NewGameParam(width, height) err = websocket.JSON.Send(ws, gameParam) if err != nil { - websocket.JSON.Send(ws, bot.NewFailure("generic server error")) + websocket.JSON.Send(ws, NewFailure("generic server error")) return nil, err } log.Printf("gameparam: %+v", gameParam) switch clientid.Type { case "robot": - var conf bot.Config + var conf Config log.Printf("got here?") for { log.Printf("%s Waiting for client to send conf ...", id) @@ -50,10 +129,10 @@ func Negociate(ws *websocket.Conn, id string, width, height float64) (*bot.Confi } // TODO: verify conf's type if conf.Stats.Valid() { - _ = websocket.JSON.Send(ws, bot.NewHandshake(id, true)) + _ = websocket.JSON.Send(ws, NewHandshake(id, true)) break } else { - _ = websocket.JSON.Send(ws, bot.NewHandshake(id, false)) + _ = websocket.JSON.Send(ws, NewHandshake(id, false)) } } conf.Name = clientid.Name diff --git a/robot.go b/robot.go new file mode 100644 index 0000000..a82049f --- /dev/null +++ b/robot.go @@ -0,0 +1,84 @@ +package main + +import ( + + v "bitbucket.org/hackerbots/vector" +) + +type Robot struct { + Id string `json:"id"` + Name string `json:"name"` + Stats Stats `json:"stats"` + TargetSpeed float64 `json:"speed"` + Speed float64 `json:"speed"` + Health int `json:"health"` + Position v.Point2d `json:"position"` + Heading v.Vector2d `json:"heading"` + MoveTo *v.Point2d `json:"move_to,omitempty"` + FireAt *v.Point2d `json:"fire_at,omitempty"` + Scanners []Scanner `json:"scanners"` +} + +type RobotSorter struct { + Robots []Robot +} + +func (s RobotSorter) Len() int { + return len(s.Robots) +} + +func (s RobotSorter) Swap(i, j int) { + s.Robots[i], s.Robots[j] = s.Robots[j], s.Robots[i] +} + +func (s RobotSorter) Less(i, j int) bool { + return s.Robots[i].Id < s.Robots[j].Id +} + +type Stats struct { + Hp int `json:"hp"` + Speed float64 `json:"speed"` + Acceleration float64 `json:"acceleration"` + WeaponRadius int `json:"weapon_radius"` + ScannerRadius int `json:"scanner_radius"` +} + +func (s Stats) Valid() bool { + total := int(s.Speed) + s.Hp + s.WeaponRadius + s.ScannerRadius + if total > 500 { + return false + } + return true +} + +type Projectile struct { + Id string `json:"id"` + Position v.Point2d `json:"position"` + MoveTo v.Point2d `json:"move_to"` + Radius int `json:"radius"` + Speed float64 `json:"speed"` + Damage int `json:"damage"` +} + +type Splosion struct { + Id string `json:"id"` + Position v.Point2d `json:"position"` + Radius int `json:"radius"` + MaxDamage int `json:"damage"` + MinDamage int `json:"damage"` + Lifespan int `json:"lifespan"` +} + +func (s *Splosion) Tick() { + s.Lifespan-- +} + +func (s *Splosion) Alive() bool { + return s.Lifespan > 0 +} + +type Instruction struct { + MoveTo *v.Point2d `json:"move_to,omitempty"` + FireAt *v.Point2d `json:"fire_at,omitempty"` + Stats Stats `json:"stats"` +} diff --git a/spectator.go b/spectator.go index b25176d..22bd2db 100644 --- a/spectator.go +++ b/spectator.go @@ -1,13 +1,12 @@ package main import ( - "bitbucket.org/hackerbots/bot" "code.google.com/p/go.net/websocket" ) type Spectator struct { ws *websocket.Conn - send chan *bot.Boardstate + send chan *Boardstate } func (s *Spectator) sender() {