moved code out of awkward bots repo

This commit is contained in:
Stephen McQuay 2013-09-27 22:27:05 -07:00
parent 92a2040d70
commit e33f2c0c4f
7 changed files with 233 additions and 38 deletions

1
.gitignore vendored
View File

@ -1,2 +1,3 @@
botserv botserv
*.swp *.swp
tags

54
game.go
View File

@ -1,7 +1,6 @@
package main package main
import ( import (
"bitbucket.org/hackerbots/bot"
v "bitbucket.org/hackerbots/vector" v "bitbucket.org/hackerbots/vector"
"log" "log"
"sort" "sort"
@ -10,11 +9,46 @@ import (
const maxPlayer = 128 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 { type game struct {
id string id string
players map[*player]bool players map[*player]bool
projectiles map[*bot.Projectile]bool projectiles map[*Projectile]bool
splosions map[*bot.Splosion]bool splosions map[*Splosion]bool
register chan *player register chan *player
unregister chan *player unregister chan *player
turn int turn int
@ -30,8 +64,8 @@ func NewGame(id string, width, height float64) *game {
id: id, id: id,
register: make(chan *player), register: make(chan *player),
unregister: make(chan *player, maxPlayer), unregister: make(chan *player, maxPlayer),
projectiles: make(map[*bot.Projectile]bool), projectiles: make(map[*Projectile]bool),
splosions: make(map[*bot.Splosion]bool), splosions: make(map[*Splosion]bool),
players: make(map[*player]bool), players: make(map[*player]bool),
turn: 0, turn: 0,
width: width, width: width,
@ -46,7 +80,7 @@ func NewGame(id string, width, height float64) *game {
func (g *game) run() { func (g *game) run() {
var t0, t1 time.Time var t0, t1 time.Time
payload := bot.NewBoardstate() payload := NewBoardstate()
for { for {
select { select {
case <-g.kill: case <-g.kill:
@ -102,7 +136,7 @@ func (g *game) run() {
} }
payload.Robots = append(payload.Robots, p.Robot) 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, payload.Projectiles = append(payload.Projectiles,
g.nudgeProjectiles()..., g.nudgeProjectiles()...,
@ -150,8 +184,8 @@ func (g *game) run() {
} }
} }
func (g *game) nudgeProjectiles() (rprojectiles []bot.Projectile) { func (g *game) nudgeProjectiles() (rprojectiles []Projectile) {
rprojectiles = make([]bot.Projectile, 0) rprojectiles = make([]Projectile, 0)
for p := range g.projectiles { for p := range g.projectiles {
newPos := v.Move(p.Position, p.MoveTo, float64(p.Speed), delta) 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) delete(g.projectiles, p)
// Spawn a splosion // Spawn a splosion
splo := &bot.Splosion{ splo := &Splosion{
Id: p.Id, Id: p.Id,
Position: p.Position, Position: p.Position,
Radius: p.Radius, Radius: p.Radius,

15
http.go
View File

@ -1,7 +1,6 @@
package main package main
import ( import (
"bitbucket.org/hackerbots/bot"
"code.google.com/p/go.net/websocket" "code.google.com/p/go.net/websocket"
"encoding/json" "encoding/json"
"fmt" "fmt"
@ -81,7 +80,7 @@ func listGames(w http.ResponseWriter, req *http.Request) {
func addPlayer(ws *websocket.Conn) { func addPlayer(ws *websocket.Conn) {
// route to appropriate game ... // route to appropriate game ...
var gid bot.GameID var gid GameID
err := websocket.JSON.Receive(ws, &gid) err := websocket.JSON.Receive(ws, &gid)
if err != nil { if err != nil {
log.Println("problem parsing the requested game id") log.Println("problem parsing the requested game id")
@ -94,7 +93,7 @@ func addPlayer(ws *websocket.Conn) {
if !ok { if !ok {
log.Println("ERROR: game not found") log.Println("ERROR: game not found")
websocket.JSON.Send(ws, bot.NewFailure("game 404")) websocket.JSON.Send(ws, NewFailure("game 404"))
return return
} }
@ -102,18 +101,18 @@ func addPlayer(ws *websocket.Conn) {
conf, err := Negociate(ws, id, game.width, game.height) conf, err := Negociate(ws, id, game.width, game.height)
if err != nil { if err != nil {
websocket.JSON.Send(ws, bot.NewFailure(err.Error())) websocket.JSON.Send(ws, NewFailure(err.Error()))
} }
if conf != nil { if conf != nil {
p := &player{ p := &player{
Robot: bot.Robot{ Robot: Robot{
Stats: conf.Stats, Stats: conf.Stats,
Id: id, Id: id,
Name: conf.Name, Name: conf.Name,
Health: conf.Stats.Hp, Health: conf.Stats.Hp,
Scanners: make([]bot.Scanner, 0)}, Scanners: make([]Scanner, 0)},
send: make(chan *bot.Boardstate), send: make(chan *Boardstate),
ws: ws, ws: ws,
} }
p.reset() 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) log.Printf("game %s: player %v has been disconnected from this game\n", gid.Id, p.Robot.Id)
} else { } else {
s := &Spectator{ s := &Spectator{
send: make(chan *bot.Boardstate), send: make(chan *Boardstate),
ws: ws, ws: ws,
} }
game.sregister <- s game.sregister <- s

View File

@ -1,7 +1,6 @@
package main package main
import ( import (
"bitbucket.org/hackerbots/bot"
v "bitbucket.org/hackerbots/vector" v "bitbucket.org/hackerbots/vector"
"code.google.com/p/go.net/websocket" "code.google.com/p/go.net/websocket"
"log" "log"
@ -11,9 +10,9 @@ import (
type player struct { type player struct {
ws *websocket.Conn ws *websocket.Conn
Robot bot.Robot Robot Robot
send chan *bot.Boardstate send chan *Boardstate
Instruction bot.Instruction Instruction Instruction
} }
func (p *player) sender() { func (p *player) sender() {
@ -31,7 +30,7 @@ func (p *player) recv() {
for { for {
// XXX: need to mark myself as having received something, also binding // XXX: need to mark myself as having received something, also binding
// such action to a particular game turn ID // such action to a particular game turn ID
var msg bot.Instruction var msg Instruction
err := websocket.JSON.Receive(p.ws, &msg) err := websocket.JSON.Receive(p.ws, &msg)
if err != nil { if err != nil {
// TODO: perhaps we could be a bit more precise in the handling of // 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) dist := v.Distance(player.Robot.Position, p.Robot.Position)
if dist < float64(p.Robot.Stats.ScannerRadius) { if dist < float64(p.Robot.Stats.ScannerRadius) {
s := bot.Scanner{ s := Scanner{
Position: v.Point2d{ Position: v.Point2d{
X: player.Robot.Position.X, X: player.Robot.Position.X,
Y: player.Robot.Position.Y, 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 // XXX: is this to prevent us from having multiple projectiles from the
// same bot? // same bot?
for proj := range projectiles { 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, Id: p.Robot.Id,
Position: p.Robot.Position, Position: p.Robot.Position,
MoveTo: *p.Robot.FireAt, MoveTo: *p.Robot.FireAt,

View File

@ -1,21 +1,100 @@
package main package main
import ( import (
"bitbucket.org/hackerbots/bot"
"code.google.com/p/go.net/websocket" "code.google.com/p/go.net/websocket"
"errors" "errors"
"log" "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 var err error
err = websocket.JSON.Send(ws, bot.NewIdRequest(id)) err = websocket.JSON.Send(ws, NewIdRequest(id))
if err != nil { if err != nil {
return nil, errors.New("generic server error") return nil, errors.New("generic server error")
} }
var clientid bot.ClientID var clientid ClientID
err = websocket.JSON.Receive(ws, &clientid) err = websocket.JSON.Receive(ws, &clientid)
if err != nil { if err != nil {
return nil, errors.New("could not parse id") 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) log.Printf("clientid is invalid: %+v", clientid)
websocket.JSON.Send( websocket.JSON.Send(
ws, ws,
bot.NewFailure(msg), NewFailure(msg),
) )
return nil, errors.New(msg) return nil, errors.New(msg)
} }
log.Printf("clientid: %+v", clientid) log.Printf("clientid: %+v", clientid)
gameParam := bot.NewGameParam(width, height) gameParam := NewGameParam(width, height)
err = websocket.JSON.Send(ws, gameParam) err = websocket.JSON.Send(ws, gameParam)
if err != nil { if err != nil {
websocket.JSON.Send(ws, bot.NewFailure("generic server error")) websocket.JSON.Send(ws, NewFailure("generic server error"))
return nil, err return nil, err
} }
log.Printf("gameparam: %+v", gameParam) log.Printf("gameparam: %+v", gameParam)
switch clientid.Type { switch clientid.Type {
case "robot": case "robot":
var conf bot.Config var conf Config
log.Printf("got here?") log.Printf("got here?")
for { for {
log.Printf("%s Waiting for client to send conf ...", id) 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 // TODO: verify conf's type
if conf.Stats.Valid() { if conf.Stats.Valid() {
_ = websocket.JSON.Send(ws, bot.NewHandshake(id, true)) _ = websocket.JSON.Send(ws, NewHandshake(id, true))
break break
} else { } else {
_ = websocket.JSON.Send(ws, bot.NewHandshake(id, false)) _ = websocket.JSON.Send(ws, NewHandshake(id, false))
} }
} }
conf.Name = clientid.Name conf.Name = clientid.Name

84
robot.go Normal file
View File

@ -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"`
}

View File

@ -1,13 +1,12 @@
package main package main
import ( import (
"bitbucket.org/hackerbots/bot"
"code.google.com/p/go.net/websocket" "code.google.com/p/go.net/websocket"
) )
type Spectator struct { type Spectator struct {
ws *websocket.Conn ws *websocket.Conn
send chan *bot.Boardstate send chan *Boardstate
} }
func (s *Spectator) sender() { func (s *Spectator) sender() {