refactor for bot
This commit is contained in:
parent
835303c53d
commit
b495b16065
48
game.go
48
game.go
@ -1,6 +1,7 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bitbucket.org/hackerbots/bot"
|
||||
"log"
|
||||
"sort"
|
||||
"time"
|
||||
@ -9,8 +10,8 @@ import (
|
||||
type game struct {
|
||||
id string
|
||||
players map[*player]bool
|
||||
projectiles map[*projectile]bool
|
||||
splosions map[*splosion]bool
|
||||
projectiles map[*bot.Projectile]bool
|
||||
splosions map[*bot.Splosion]bool
|
||||
register chan *player
|
||||
unregister chan *player
|
||||
robot_id chan int
|
||||
@ -27,8 +28,8 @@ func NewGame(id string, width, height float64) *game {
|
||||
id: id,
|
||||
register: make(chan *player),
|
||||
unregister: make(chan *player),
|
||||
projectiles: make(map[*projectile]bool),
|
||||
splosions: make(map[*splosion]bool),
|
||||
projectiles: make(map[*bot.Projectile]bool),
|
||||
splosions: make(map[*bot.Splosion]bool),
|
||||
players: make(map[*player]bool),
|
||||
turn: 0,
|
||||
width: width,
|
||||
@ -40,29 +41,6 @@ func NewGame(id string, width, height float64) *game {
|
||||
}
|
||||
}
|
||||
|
||||
type Config struct {
|
||||
ID string `json:"id"`
|
||||
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 NewBoardstate(id int) *boardstate {
|
||||
return &boardstate{
|
||||
Robots: []robot{},
|
||||
Projectiles: []projectile{},
|
||||
Type: "boardstate",
|
||||
Turn: id,
|
||||
}
|
||||
}
|
||||
|
||||
func (g *game) run() {
|
||||
g.robot_id = make(chan int)
|
||||
go func() {
|
||||
@ -88,13 +66,15 @@ func (g *game) run() {
|
||||
case <-time.Tick(time.Duration(*tick) * time.Millisecond):
|
||||
g.turn++
|
||||
t0 := time.Now()
|
||||
|
||||
if *verbose {
|
||||
log.Printf("\033[2JTurn: %v", g.turn)
|
||||
log.Printf("Players: %v", len(g.players))
|
||||
log.Printf("Projectiles: %v", len(g.projectiles))
|
||||
log.Printf("Explosions: %v", len(g.splosions))
|
||||
}
|
||||
payload := NewBoardstate(g.turn)
|
||||
|
||||
payload := bot.NewBoardstate(g.turn)
|
||||
|
||||
robots_remaining := 0
|
||||
|
||||
@ -103,21 +83,25 @@ func (g *game) run() {
|
||||
robots_remaining++
|
||||
p.scan()
|
||||
p.nudge()
|
||||
// XXX: change to pointer, check for pointer as (0, 0) is valid target
|
||||
if p.Robot.FireAt.X != 0 && p.Robot.FireAt.Y != 0 {
|
||||
p.fire()
|
||||
proj := p.fire()
|
||||
if proj != nil {
|
||||
g.projectiles[proj] = true
|
||||
}
|
||||
}
|
||||
}
|
||||
payload.Robots = append(payload.Robots, p.Robot)
|
||||
}
|
||||
sort.Sort(robotSorter{payload.Robots})
|
||||
sort.Sort(bot.RobotSorter{payload.Robots})
|
||||
|
||||
for p := range g.projectiles {
|
||||
p.nudge()
|
||||
// XXX: p.nudge()
|
||||
payload.Projectiles = append(payload.Projectiles, *p)
|
||||
}
|
||||
|
||||
for s := range g.splosions {
|
||||
s.tick()
|
||||
// XXX: s.tick()
|
||||
payload.Splosions = append(payload.Splosions, *s)
|
||||
}
|
||||
|
||||
|
24
http.go
24
http.go
@ -1,6 +1,7 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bitbucket.org/hackerbots/bot"
|
||||
"code.google.com/p/go.net/websocket"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
@ -23,6 +24,7 @@ func startGame(w http.ResponseWriter, req *http.Request) {
|
||||
|
||||
gameLock.Lock()
|
||||
games[new_game_name] = _g
|
||||
log.Printf("%+v", games)
|
||||
gameLock.Unlock()
|
||||
|
||||
w.Write([]byte(fmt.Sprintf(`{"id": "%s"}`, new_game_name)))
|
||||
@ -42,7 +44,7 @@ func listGames(w http.ResponseWriter, req *http.Request) {
|
||||
|
||||
func addPlayer(ws *websocket.Conn) {
|
||||
// route to appropriate game ...
|
||||
var gid GameID
|
||||
var gid bot.GameID
|
||||
err := websocket.JSON.Receive(ws, &gid)
|
||||
if err != nil {
|
||||
log.Println("problem parsing the requested game id")
|
||||
@ -50,11 +52,15 @@ func addPlayer(ws *websocket.Conn) {
|
||||
}
|
||||
|
||||
gameLock.Lock()
|
||||
game := games[gid.Id]
|
||||
game, ok := games[gid.Id]
|
||||
gameLock.Unlock()
|
||||
log.Printf("%+v", game)
|
||||
|
||||
id := "asdf"
|
||||
if !ok {
|
||||
log.Println("ERROR: game not found")
|
||||
return
|
||||
}
|
||||
|
||||
id := idg.Hash()
|
||||
|
||||
conf, err := Negociate(ws, id, 400, 800)
|
||||
if err != nil {
|
||||
@ -63,15 +69,17 @@ func addPlayer(ws *websocket.Conn) {
|
||||
|
||||
if conf != nil {
|
||||
p := &player{
|
||||
Robot: robot{
|
||||
Robot: bot.Robot{
|
||||
Stats: conf.Stats,
|
||||
Id: id,
|
||||
Health: conf.Stats.Hp,
|
||||
Scanners: make([]scanner, 0)},
|
||||
send: make(chan *boardstate),
|
||||
Scanners: make([]bot.Scanner, 0)},
|
||||
send: make(chan *bot.Boardstate),
|
||||
ws: ws,
|
||||
game: game,
|
||||
}
|
||||
p.reset()
|
||||
log.Printf("game: %+v", game)
|
||||
game.register <- p
|
||||
defer func() {
|
||||
game.unregister <- p
|
||||
@ -81,7 +89,7 @@ func addPlayer(ws *websocket.Conn) {
|
||||
log.Printf("%v has been disconnect from this game\n", p.Robot.Id)
|
||||
} else {
|
||||
s := &Spectator{
|
||||
send: make(chan *boardstate),
|
||||
send: make(chan *bot.Boardstate),
|
||||
ws: ws,
|
||||
}
|
||||
game.sregister <- s
|
||||
|
24
player.go
24
player.go
@ -1,6 +1,7 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bitbucket.org/hackerbots/bot"
|
||||
v "bitbucket.org/hackerbots/vector"
|
||||
"code.google.com/p/go.net/websocket"
|
||||
"log"
|
||||
@ -9,15 +10,16 @@ import (
|
||||
|
||||
type player struct {
|
||||
ws *websocket.Conn
|
||||
Robot robot
|
||||
send chan *boardstate
|
||||
game *game
|
||||
Robot bot.Robot
|
||||
send chan *bot.Boardstate
|
||||
Instruction instruction
|
||||
}
|
||||
|
||||
type instruction struct {
|
||||
MoveTo *v.Point2d `json:"move_to,omitempty"`
|
||||
FireAt *v.Point2d `json:"fire_at,omitempty"`
|
||||
Stats stats `json:"stats"`
|
||||
Stats bot.Stats `json:"stats"`
|
||||
}
|
||||
|
||||
func (p *player) sender() {
|
||||
@ -60,14 +62,14 @@ func (p *player) nudge() {
|
||||
}
|
||||
|
||||
func (p *player) scan() {
|
||||
p.Robot.Scanners = make([]scanner, 0)
|
||||
for player := range g.players {
|
||||
p.Robot.Scanners = make([]bot.Scanner, 0)
|
||||
for player := range p.game.players {
|
||||
if player.Robot.Id == p.Robot.Id || player.Robot.Health <= 0 {
|
||||
continue
|
||||
}
|
||||
dist := distance(player.Robot.Position, p.Robot.Position)
|
||||
if dist < float64(p.Robot.Stats.ScannerRadius) {
|
||||
s := scanner{
|
||||
s := bot.Scanner{
|
||||
Position: v.Point2d{
|
||||
X: player.Robot.Position.X,
|
||||
Y: player.Robot.Position.Y,
|
||||
@ -78,15 +80,14 @@ func (p *player) scan() {
|
||||
}
|
||||
}
|
||||
|
||||
func (p *player) fire() {
|
||||
|
||||
for proj := range g.projectiles {
|
||||
func (p *player) fire() *bot.Projectile {
|
||||
for proj := range p.game.projectiles {
|
||||
if proj.Id == p.Robot.Id {
|
||||
return
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
proj := &projectile{
|
||||
return &bot.Projectile{
|
||||
Id: p.Robot.Id,
|
||||
Position: p.Robot.Position,
|
||||
MoveTo: p.Robot.FireAt,
|
||||
@ -94,7 +95,6 @@ func (p *player) fire() {
|
||||
Radius: p.Robot.Stats.WeaponRadius,
|
||||
Speed: float64(p.Robot.Stats.Speed * 2),
|
||||
}
|
||||
g.projectiles[proj] = true
|
||||
}
|
||||
|
||||
func (p *player) reset() {
|
||||
|
84
protocol.go
84
protocol.go
@ -1,77 +1,11 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bitbucket.org/hackerbots/bot"
|
||||
"code.google.com/p/go.net/websocket"
|
||||
"errors"
|
||||
)
|
||||
|
||||
type GameID struct {
|
||||
Id string `json:"id"`
|
||||
}
|
||||
|
||||
// > identify
|
||||
type IdRequest struct {
|
||||
Type string `json:"type"`
|
||||
AssignedID string `json:"id"`
|
||||
}
|
||||
|
||||
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"`
|
||||
@ -84,15 +18,15 @@ func NewFailure(reason string) *Failure {
|
||||
}
|
||||
}
|
||||
|
||||
func Negociate(ws *websocket.Conn, id string, width, height float64) (*Config, error) {
|
||||
func Negociate(ws *websocket.Conn, id string, width, height float64) (*bot.Config, error) {
|
||||
var err error
|
||||
|
||||
err = websocket.JSON.Send(ws, NewIdRequest(id))
|
||||
err = websocket.JSON.Send(ws, bot.NewIdRequest(id))
|
||||
if err != nil {
|
||||
return nil, errors.New("generic servr error")
|
||||
}
|
||||
|
||||
var clientid ClientID
|
||||
var clientid bot.ClientID
|
||||
err = websocket.JSON.Receive(ws, &clientid)
|
||||
if err != nil {
|
||||
return nil, errors.New("could not parse id")
|
||||
@ -105,7 +39,7 @@ func Negociate(ws *websocket.Conn, id string, width, height float64) (*Config, e
|
||||
return nil, errors.New(msg)
|
||||
}
|
||||
|
||||
gameParam := NewGameParam(width, height)
|
||||
gameParam := bot.NewGameParam(width, height)
|
||||
err = websocket.JSON.Send(ws, gameParam)
|
||||
if err != nil {
|
||||
websocket.JSON.Send(ws, NewFailure("generic server error"))
|
||||
@ -113,18 +47,18 @@ func Negociate(ws *websocket.Conn, id string, width, height float64) (*Config, e
|
||||
}
|
||||
switch clientid.Type {
|
||||
case "robot":
|
||||
var conf Config
|
||||
var conf bot.Config
|
||||
for {
|
||||
err = websocket.JSON.Receive(ws, &conf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// TODO: verify conf's type
|
||||
if conf.Stats.valid() {
|
||||
_ = websocket.JSON.Send(ws, NewHandshake(id, true))
|
||||
if conf.Stats.Valid() {
|
||||
_ = websocket.JSON.Send(ws, bot.NewHandshake(id, true))
|
||||
break
|
||||
} else {
|
||||
_ = websocket.JSON.Send(ws, NewHandshake(id, false))
|
||||
_ = websocket.JSON.Send(ws, bot.NewHandshake(id, false))
|
||||
}
|
||||
}
|
||||
return &conf, nil
|
||||
|
@ -3,6 +3,7 @@ package main
|
||||
import (
|
||||
"code.google.com/p/go.net/websocket"
|
||||
"encoding/json"
|
||||
"log"
|
||||
"net/http"
|
||||
"testing"
|
||||
)
|
||||
@ -17,6 +18,10 @@ type clientIDTest struct {
|
||||
expected result
|
||||
}
|
||||
|
||||
type CreatedGame struct {
|
||||
Id string `json:"id"`
|
||||
}
|
||||
|
||||
var clientIDTests = []clientIDTest{
|
||||
{ClientID{Type: "robot"}, result{true, ""}},
|
||||
{ClientID{Type: "spectator"}, result{true, ""}},
|
||||
@ -52,11 +57,13 @@ var handled bool
|
||||
func DummyServer() (*websocket.Conn, error) {
|
||||
if !handled {
|
||||
http.Handle("/ws/", websocket.Handler(addPlayer))
|
||||
http.Handle("/game/start/", JsonHandler(startGame))
|
||||
handled = true
|
||||
}
|
||||
|
||||
g = NewGame("hello world", 400, 800)
|
||||
go g.run()
|
||||
games = make(map[string]*game)
|
||||
idg = NewIdGenerator()
|
||||
|
||||
go http.ListenAndServe(*addr, nil)
|
||||
|
||||
origin := "http://localhost/"
|
||||
@ -66,6 +73,18 @@ func DummyServer() (*websocket.Conn, error) {
|
||||
|
||||
func TestGoodProtocol(t *testing.T) {
|
||||
ws, err := DummyServer()
|
||||
log.Printf("blah: %+v", ws)
|
||||
|
||||
resp, err := http.Get("http://localhost:8666/game/start/")
|
||||
if err != nil {
|
||||
t.Errorf("error getting: %+v", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
var g CreatedGame
|
||||
if err := json.NewDecoder(resp.Body).Decode(&g); err != nil {
|
||||
t.Errorf("json parse error? %+v", err)
|
||||
}
|
||||
log.Printf("g: %+v\n", g)
|
||||
|
||||
err = websocket.JSON.Send(ws, GameID{
|
||||
"test",
|
||||
|
164
robot.go
164
robot.go
@ -1,126 +1,52 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
v "bitbucket.org/hackerbots/vector"
|
||||
)
|
||||
|
||||
type weapon struct {
|
||||
Strength float64 `json:"strength"`
|
||||
Radius float64 `json:"radius"`
|
||||
}
|
||||
|
||||
type stats struct {
|
||||
Hp int `json:"hp"`
|
||||
Speed float64 `json:"speed"`
|
||||
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 scanner struct {
|
||||
Position v.Point2d `json:"position"`
|
||||
Stats stats `json:"stats"`
|
||||
}
|
||||
|
||||
type robot struct {
|
||||
Id string `json:"id"`
|
||||
Stats stats `json:"stats"`
|
||||
Health int `json:"health"`
|
||||
Position v.Point2d `json:"position"`
|
||||
MoveTo v.Point2d `json:"move_to"`
|
||||
FireAt v.Point2d `json:"fire_at"`
|
||||
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 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"`
|
||||
}
|
||||
|
||||
func (p *projectile) nudge() {
|
||||
newPos := move(p.Position, p.MoveTo, float64(p.Speed), delta)
|
||||
|
||||
hit_player := false
|
||||
for player := range g.players {
|
||||
if player.Robot.Id == p.Id {
|
||||
continue
|
||||
}
|
||||
dist := distance(player.Robot.Position, p.Position)
|
||||
if dist < 5.0 {
|
||||
hit_player = true
|
||||
}
|
||||
}
|
||||
|
||||
if distance(p.Position, p.MoveTo) < 5 || hit_player {
|
||||
delete(g.projectiles, p)
|
||||
|
||||
// Spawn a splosion
|
||||
splo := &splosion{
|
||||
Id: p.Id,
|
||||
Position: p.Position,
|
||||
Radius: p.Radius,
|
||||
MaxDamage: 10,
|
||||
MinDamage: 5,
|
||||
Lifespan: 8,
|
||||
}
|
||||
g.splosions[splo] = true
|
||||
|
||||
for player := range g.players {
|
||||
dist := distance(player.Robot.Position, p.Position)
|
||||
if dist < float64(p.Radius) {
|
||||
|
||||
// TODO map damage Max to Min based on distance from explosion
|
||||
if player.Robot.Health > 0 {
|
||||
player.Robot.Health -= p.Damage
|
||||
if player.Robot.Health <= 0 {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
p.Position.X = newPos.X
|
||||
p.Position.Y = newPos.Y
|
||||
}
|
||||
|
||||
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--
|
||||
if s.Lifespan <= 0 {
|
||||
delete(g.splosions, s)
|
||||
}
|
||||
}
|
||||
// XXX: this needs to go into game somehow
|
||||
// func (p *bot.Projectile) nudge() {
|
||||
// newPos := move(p.Position, p.MoveTo, float64(p.Speed), delta)
|
||||
//
|
||||
// hit_player := false
|
||||
// for player := range p.game.players {
|
||||
// if player.Robot.Id == p.Id {
|
||||
// continue
|
||||
// }
|
||||
// dist := distance(player.Robot.Position, p.Position)
|
||||
// if dist < 5.0 {
|
||||
// hit_player = true
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// if distance(p.Position, p.MoveTo) < 5 || hit_player {
|
||||
// delete(p.game.projectiles, p)
|
||||
//
|
||||
// // Spawn a splosion
|
||||
// splo := &splosion{
|
||||
// Id: p.Id,
|
||||
// Position: p.Position,
|
||||
// Radius: p.Radius,
|
||||
// MaxDamage: 10,
|
||||
// MinDamage: 5,
|
||||
// Lifespan: 8,
|
||||
// }
|
||||
// p.game.splosions[splo] = true
|
||||
//
|
||||
// for player := range p.game.players {
|
||||
// dist := distance(player.Robot.Position, p.Position)
|
||||
// if dist < float64(p.Radius) {
|
||||
//
|
||||
// // TODO map damage Max to Min based on distance from explosion
|
||||
// if player.Robot.Health > 0 {
|
||||
// player.Robot.Health -= p.Damage
|
||||
// if player.Robot.Health <= 0 {
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// p.Position.X = newPos.X
|
||||
// p.Position.Y = newPos.Y
|
||||
// }
|
||||
|
@ -1,12 +1,13 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bitbucket.org/hackerbots/bot"
|
||||
"code.google.com/p/go.net/websocket"
|
||||
)
|
||||
|
||||
type Spectator struct {
|
||||
ws *websocket.Conn
|
||||
send chan *boardstate
|
||||
send chan *bot.Boardstate
|
||||
}
|
||||
|
||||
func (s *Spectator) sender() {
|
||||
|
Loading…
Reference in New Issue
Block a user