ignore new binary

Create new bserv binary

Herein lie the following ideas:

- change package visibility of many things
- try to stash the globals into structs
- the code is far from correct; it merely compiles
This commit is contained in:
Stephen McQuay 2014-03-18 23:38:06 -07:00
parent 62222a9ad9
commit c8efd34080
16 changed files with 146 additions and 126 deletions

2
.gitignore vendored
View File

@ -1,3 +1,3 @@
botserv bserv/bserv
*.swp *.swp
tags tags

View File

@ -9,6 +9,7 @@ import (
"os" "os"
"runtime/pprof" "runtime/pprof"
"time" "time"
"bitbucket.org/hackerbots/botserv"
"code.google.com/p/go.net/websocket" "code.google.com/p/go.net/websocket"
) )
@ -22,12 +23,6 @@ var config = flag.String("config", "~/.config/hackerbots/config.json", "location
var delta float32 var delta float32
var idg *IdGenerator
var conf Config
// This is the main, global collection of games
var games MapLock
func main() { func main() {
log.SetFlags(log.Ldate | log.Ltime | log.Lshortfile) log.SetFlags(log.Ldate | log.Ltime | log.Lshortfile)
log.Printf("Starting Server...") log.Printf("Starting Server...")
@ -49,10 +44,7 @@ func main() {
log.Println("serving profile info at http://localhost:8667/debug/pprof/") log.Println("serving profile info at http://localhost:8667/debug/pprof/")
} }
games = MapLock{m: make(map[string]*game)} conf, err := botserv.LoadConfig(*config)
idg = NewIdGenerator()
conf, err = loadConfig(*config)
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }
@ -61,14 +53,24 @@ func main() {
sm := http.NewServeMux() sm := http.NewServeMux()
sm.Handle("/", JsonHandler(index)) c := botserv.Controller{
sm.Handle("/ws/", websocket.Handler(addPlayer)) Idg: botserv.NewIdGenerator(),
sm.Handle("/game/start/", JsonHandler(startGame)) Conf: conf,
sm.Handle("/game/list/", JsonHandler(listGames)) Games: botserv.MapLock{
sm.Handle("/game/stats/", JsonHandler(gameStats)) M: make(map[string]*botserv.Game),
sm.Handle("/game/bw/", JsonHandler(bw)) },
sm.Handle("/game/stop/", JsonHandler(stopGame)) Memprofile: *mprofile,
sm.HandleFunc("/fuck/shit/up/", killServer) Profile: *profile,
}
sm.Handle("/", botserv.JsonHandler(c.Index))
sm.Handle("/ws/", websocket.Handler(c.AddPlayer))
sm.Handle("/game/start/", botserv.JsonHandler(c.StartGame))
sm.Handle("/game/list/", botserv.JsonHandler(c.ListGames))
sm.Handle("/game/stats/", botserv.JsonHandler(c.GameStats))
sm.Handle("/game/bw/", botserv.JsonHandler(c.BW))
sm.Handle("/game/stop/", botserv.JsonHandler(c.StopGame))
sm.HandleFunc("/fuck/shit/up/", c.KillServer)
err = http.ListenAndServe(*addr, sm) err = http.ListenAndServe(*addr, sm)
if err != nil { if err != nil {

View File

@ -1,4 +1,4 @@
package main package botserv
import ( import (
"encoding/json" "encoding/json"
@ -31,7 +31,7 @@ const (
DEFAULT_MODE = "deathmatch" DEFAULT_MODE = "deathmatch"
) )
func loadConfig(filename string) (Config, error) { func LoadConfig(filename string) (Config, error) {
c := Config{ c := Config{
Tick: TICK, Tick: TICK,
Timescale: TIMESCALE, Timescale: TIMESCALE,

View File

@ -1,4 +1,4 @@
package main package botserv
import ( import (
"encoding/json" "encoding/json"
@ -19,13 +19,21 @@ func (h JsonHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
h(w, req) h(w, req)
} }
func startGame(w http.ResponseWriter, req *http.Request) { type Controller struct {
Idg *IdGenerator
Conf Config
Games MapLock
Memprofile string
Profile string
}
func (c *Controller) StartGame(w http.ResponseWriter, req *http.Request) {
log.Println("asked to create a game") log.Println("asked to create a game")
requested_game_name := idg.Hash() requested_game_name := c.Idg.Hash()
width, height := float32(conf.Width), float32(conf.Height) width, height := float32(c.Conf.Width), float32(c.Conf.Height)
obstacles := 0 obstacles := 0
maxPoints := conf.MaxPoints maxPoints := c.Conf.MaxPoints
mode := "deathmatch" mode := "deathmatch"
// here we determine if we are going to run with defaults or pick them off // here we determine if we are going to run with defaults or pick them off
@ -55,10 +63,10 @@ func startGame(w http.ResponseWriter, req *http.Request) {
mode = cfg.Mode mode = cfg.Mode
} }
g := games.get(requested_game_name) g := c.Games.get(requested_game_name)
if g == nil { if g == nil {
log.Printf("Game '%s' non-existant; making it now", requested_game_name) log.Printf("Game '%s' non-existant; making it now", requested_game_name)
g, err := NewGame(requested_game_name, width, height, obstacles, conf.Tick, maxPoints, mode) g, err := NewGame(requested_game_name, width, height, obstacles, c.Conf.Tick, maxPoints, mode)
if err != nil { if err != nil {
log.Printf("problem creating game: %s: %s", requested_game_name, err) log.Printf("problem creating game: %s: %s", requested_game_name, err)
b, _ := json.Marshal(NewFailure("game creation failure")) b, _ := json.Marshal(NewFailure("game creation failure"))
@ -66,7 +74,7 @@ func startGame(w http.ResponseWriter, req *http.Request) {
return return
} }
go g.run() go g.run()
games.add(g) c.Games.add(g)
} else { } else {
log.Printf("Game '%s' already exists: %p", requested_game_name, g) log.Printf("Game '%s' already exists: %p", requested_game_name, g)
b, _ := json.Marshal(NewFailure("game already exists")) b, _ := json.Marshal(NewFailure("game already exists"))
@ -84,10 +92,10 @@ func startGame(w http.ResponseWriter, req *http.Request) {
} }
} }
func listGames(w http.ResponseWriter, req *http.Request) { func (c *Controller) ListGames(w http.ResponseWriter, req *http.Request) {
log.Println("games list requested") log.Println("games list requested")
games.RLock() c.Games.RLock()
defer games.RUnlock() defer c.Games.RUnlock()
type pout struct { type pout struct {
Name string `json:"name"` Name string `json:"name"`
Id string `json:"id"` Id string `json:"id"`
@ -97,7 +105,7 @@ func listGames(w http.ResponseWriter, req *http.Request) {
Players []pout `json:"players"` Players []pout `json:"players"`
} }
ids := make([]gl, 0) ids := make([]gl, 0)
for id, g := range games.m { for id, g := range c.Games.M {
players := make([]pout, 0) players := make([]pout, 0)
// TODO - players instead of robots? // TODO - players instead of robots?
for p := range g.players { for p := range g.players {
@ -118,19 +126,19 @@ func listGames(w http.ResponseWriter, req *http.Request) {
} }
} }
func gameStats(w http.ResponseWriter, req *http.Request) { func (c *Controller) GameStats(w http.ResponseWriter, req *http.Request) {
// TODO: wrap this up in something similar to the JsonHandler to verify the // TODO: wrap this up in something similar to the JsonHandler to verify the
// url? Look at gorilla routing? // url? Look at gorilla routing?
key, err := getGameId(req.URL.Path) key, err := c.getGameId(req.URL.Path)
if err != nil { if err != nil {
b, _ := json.Marshal(NewFailure(err.Error())) b, _ := json.Marshal(NewFailure(err.Error()))
http.Error(w, string(b), http.StatusBadRequest) http.Error(w, string(b), http.StatusBadRequest)
return return
} }
log.Printf("requested stats for game: %s", key) log.Printf("requested stats for game: %s", key)
games.RLock() c.Games.RLock()
g, ok := games.m[key] g, ok := c.Games.M[key]
games.RUnlock() c.Games.RUnlock()
if !ok { if !ok {
b, _ := json.Marshal(NewFailure("game not found")) b, _ := json.Marshal(NewFailure("game not found"))
http.Error(w, string(b), http.StatusNotFound) http.Error(w, string(b), http.StatusNotFound)
@ -143,19 +151,19 @@ func gameStats(w http.ResponseWriter, req *http.Request) {
} }
} }
func bw(w http.ResponseWriter, req *http.Request) { func (c *Controller) BW(w http.ResponseWriter, req *http.Request) {
// TODO: wrap this up in something similar to the JsonHandler to verify the // TODO: wrap this up in something similar to the JsonHandler to verify the
// url? Look at gorilla routing? // url? Look at gorilla routing?
key, err := getGameId(req.URL.Path) key, err := c.getGameId(req.URL.Path)
if err != nil { if err != nil {
b, _ := json.Marshal(NewFailure(err.Error())) b, _ := json.Marshal(NewFailure(err.Error()))
http.Error(w, string(b), http.StatusBadRequest) http.Error(w, string(b), http.StatusBadRequest)
return return
} }
log.Printf("requested bandwidth for game: %s", key) log.Printf("requested bandwidth for game: %s", key)
games.RLock() c.Games.RLock()
g, ok := games.m[key] g, ok := c.Games.M[key]
games.RUnlock() c.Games.RUnlock()
if !ok { if !ok {
b, _ := json.Marshal(NewFailure("game not found")) b, _ := json.Marshal(NewFailure("game not found"))
http.Error(w, string(b), http.StatusNotFound) http.Error(w, string(b), http.StatusNotFound)
@ -170,16 +178,16 @@ func bw(w http.ResponseWriter, req *http.Request) {
} }
} }
func stopGame(w http.ResponseWriter, req *http.Request) { func (c *Controller) StopGame(w http.ResponseWriter, req *http.Request) {
key, err := getGameId(req.URL.Path) key, err := c.getGameId(req.URL.Path)
if err != nil { if err != nil {
b, _ := json.Marshal(NewFailure(err.Error())) b, _ := json.Marshal(NewFailure(err.Error()))
http.Error(w, string(b), http.StatusBadRequest) http.Error(w, string(b), http.StatusBadRequest)
return return
} }
games.Lock() c.Games.Lock()
g, ok := games.m[key] g, ok := c.Games.M[key]
defer games.Unlock() defer c.Games.Unlock()
if !ok { if !ok {
http.NotFound(w, req) http.NotFound(w, req)
return return
@ -197,15 +205,15 @@ func stopGame(w http.ResponseWriter, req *http.Request) {
} }
} }
func killServer(w http.ResponseWriter, req *http.Request) { func (c *Controller) KillServer(w http.ResponseWriter, req *http.Request) {
if *profile != "" { if c.Profile != "" {
log.Print("trying to stop cpu profile") log.Print("trying to stop cpu profile")
pprof.StopCPUProfile() pprof.StopCPUProfile()
log.Print("stopped cpu profile") log.Print("stopped cpu profile")
} }
if *mprofile != "" { if c.Memprofile != "" {
log.Print("trying to dump memory profile") log.Print("trying to dump memory profile")
f, err := os.Create(*mprofile) f, err := os.Create(c.Memprofile)
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }
@ -216,7 +224,7 @@ func killServer(w http.ResponseWriter, req *http.Request) {
log.Fatal("shit got fucked up") log.Fatal("shit got fucked up")
} }
func index(w http.ResponseWriter, req *http.Request) { func (c *Controller) Index(w http.ResponseWriter, req *http.Request) {
log.Println("version requested") log.Println("version requested")
version := struct { version := struct {
Version string `json:"version"` Version string `json:"version"`
@ -230,7 +238,7 @@ func index(w http.ResponseWriter, req *http.Request) {
} }
} }
func getGameId(path string) (string, error) { func (c *Controller) getGameId(path string) (string, error) {
var err error var err error
trimmed := strings.Trim(path, "/") trimmed := strings.Trim(path, "/")
fullPath := strings.Split(trimmed, "/") fullPath := strings.Split(trimmed, "/")

View File

@ -1,4 +1,4 @@
package main package botserv
import ( import (
"log" "log"
@ -7,10 +7,10 @@ import (
type deathmatch struct { type deathmatch struct {
} }
func (g *deathmatch) setup(gg *game) { func (g *deathmatch) setup(gg *Game) {
} }
func (g *deathmatch) gameOver(gg *game) (bool, *GameOver) { func (g *deathmatch) gameOver(gg *Game) (bool, *GameOver) {
over := false over := false
var stats *GameOver var stats *GameOver
@ -41,5 +41,5 @@ func (g *deathmatch) gameOver(gg *game) (bool, *GameOver) {
return over, stats return over, stats
} }
func (g *deathmatch) tick(gg *game, payload *Boardstate) { func (g *deathmatch) tick(gg *Game, payload *Boardstate) {
} }

51
game.go
View File

@ -1,4 +1,4 @@
package main package botserv
// delete me // delete me
@ -24,23 +24,23 @@ type Scanner struct {
} }
type MapLock struct { type MapLock struct {
m map[string]*game M map[string]*Game
sync.RWMutex sync.RWMutex
} }
// get is a function that returns a game if found, and creates one if // get is a function that returns a game if found, and creates one if
// not found and force is true. In order to get a hash (rather than use // not found and force is true. In order to get a hash (rather than use
// the string you pass) send "" for id. // the string you pass) send "" for id.
func (ml *MapLock) get(id string) *game { func (ml *MapLock) get(id string) *Game {
ml.Lock() ml.Lock()
g, _ := ml.m[id] g, _ := ml.M[id]
ml.Unlock() ml.Unlock()
return g return g
} }
func (ml *MapLock) add(g *game) { func (ml *MapLock) add(g *Game) {
ml.Lock() ml.Lock()
ml.m[g.id] = g ml.M[g.id] = g
ml.Unlock() ml.Unlock()
} }
@ -64,7 +64,7 @@ type GameStats struct {
sync.RWMutex sync.RWMutex
} }
type game struct { type Game struct {
id string id string
players map[*player]bool players map[*player]bool
projectiles map[*Projectile]bool projectiles map[*Projectile]bool
@ -87,15 +87,18 @@ type game struct {
stats GameStats stats GameStats
mode GameMode mode GameMode
bw *bandwidth.Bandwidth bw *bandwidth.Bandwidth
Conf Config
Games *MapLock
Verbose bool
} }
type GameMode interface { type GameMode interface {
setup(g *game) setup(g *Game)
tick(gg *game, payload *Boardstate) tick(gg *Game, payload *Boardstate)
gameOver(gg *game) (bool, *GameOver) gameOver(gg *Game) (bool, *GameOver)
} }
func NewGame(id string, width, height float32, obstacles, tick, maxPoints int, mode string) (*game, error) { func NewGame(id string, width, height float32, obstacles, tick, maxPoints int, mode string) (*Game, error) {
bw, err := bandwidth.NewBandwidth( bw, err := bandwidth.NewBandwidth(
[]int{1, 10, 60}, []int{1, 10, 60},
1*time.Second, 1*time.Second,
@ -105,7 +108,7 @@ func NewGame(id string, width, height float32, obstacles, tick, maxPoints int, m
return nil, err return nil, err
} }
go bw.Run() go bw.Run()
g := &game{ g := &Game{
id: id, id: id,
register: make(chan *player, maxPlayer), register: make(chan *player, maxPlayer),
unregister: make(chan *player, maxPlayer), unregister: make(chan *player, maxPlayer),
@ -141,7 +144,7 @@ func NewGame(id string, width, height float32, obstacles, tick, maxPoints int, m
return g, nil return g, nil
} }
func (g *game) tick(payload *Boardstate) { func (g *Game) tick(payload *Boardstate) {
g.players_remaining = 0 g.players_remaining = 0
payload.Objects = MinifyObstacles(g.obstacles) payload.Objects = MinifyObstacles(g.obstacles)
@ -196,7 +199,7 @@ func (g *game) tick(payload *Boardstate) {
} }
} }
func (g *game) sendUpdate(payload *Boardstate) { func (g *Game) sendUpdate(payload *Boardstate) {
// Ensure that the robots are always sent in a consistent order // Ensure that the robots are always sent in a consistent order
sort.Sort(RobotSorter{Robots: payload.OtherRobots}) sort.Sort(RobotSorter{Robots: payload.OtherRobots})
sort.Sort(AllRobotSorter{Robots: payload.AllBots}) sort.Sort(AllRobotSorter{Robots: payload.AllBots})
@ -292,20 +295,20 @@ func (g *game) sendUpdate(payload *Boardstate) {
} }
func (g *game) run() { func (g *Game) run() {
var t0, t1 time.Time var t0, t1 time.Time
ticker := time.NewTicker(time.Duration(conf.Tick) * time.Millisecond) ticker := time.NewTicker(time.Duration(g.Conf.Tick) * time.Millisecond)
for { for {
select { select {
case <-g.kill: case <-g.kill:
log.Printf("game %s: received kill signal, dying gracefully", g.id) log.Printf("game %s: received kill signal, dying gracefully", g.id)
close(g.bw.Quit) close(g.bw.Quit)
games.Lock() g.Games.Lock()
for player := range g.players { for player := range g.players {
close(player.send) close(player.send)
} }
delete(games.m, g.id) delete(g.Games.M, g.id)
games.Unlock() g.Games.Unlock()
return return
case p := <-g.register: case p := <-g.register:
g.players[p] = true g.players[p] = true
@ -330,7 +333,7 @@ func (g *game) run() {
g.turn++ g.turn++
payload.Turn = g.turn payload.Turn = g.turn
if *verbose { if g.Verbose {
log.Printf("\033[2JTurn: %v", g.turn) log.Printf("\033[2JTurn: %v", g.turn)
log.Printf("Players: %v", len(g.players)) log.Printf("Players: %v", len(g.players))
log.Printf("Projectiles: %v", len(g.projectiles)) log.Printf("Projectiles: %v", len(g.projectiles))
@ -346,7 +349,7 @@ func (g *game) run() {
g.mode.tick(g, payload) g.mode.tick(g, payload)
t1 = time.Now() t1 = time.Now()
if *verbose { if g.Verbose {
log.Printf("Turn Processes %v\n", t1.Sub(t0)) log.Printf("Turn Processes %v\n", t1.Sub(t0))
} }
@ -354,14 +357,14 @@ func (g *game) run() {
g.sendUpdate(payload) g.sendUpdate(payload)
t1 = time.Now() t1 = time.Now()
if *verbose { if g.Verbose {
log.Printf("Sent Payload %v\n", t1.Sub(t0)) log.Printf("Sent Payload %v\n", t1.Sub(t0))
} }
} }
} }
} }
func (g *game) sendGameOver(eg *GameOver) { func (g *Game) sendGameOver(eg *GameOver) {
log.Printf("sending out game over message: %+v", eg) log.Printf("sending out game over message: %+v", eg)
for p := range g.players { for p := range g.players {
p.send <- eg p.send <- eg
@ -373,7 +376,7 @@ func (g *game) sendGameOver(eg *GameOver) {
// returns a GameParam object popuplated by info from the game. This is // returns a GameParam object popuplated by info from the game. This is
// used during client/server initial negociation. // used during client/server initial negociation.
func (g *game) gameParam() *GameParam { func (g *Game) gameParam() *GameParam {
return &GameParam{ return &GameParam{
BoardSize: BoardSize{ BoardSize: BoardSize{
Width: g.width, Width: g.width,

2
id.go
View File

@ -1,4 +1,4 @@
package main package botserv
import ( import (
"crypto/md5" "crypto/md5"

View File

@ -1,4 +1,4 @@
package main package botserv
import ( import (
"testing" "testing"

View File

@ -1,4 +1,4 @@
package main package botserv
import ( import (
"log" "log"
@ -9,15 +9,15 @@ type melee struct {
respawn_timer float64 respawn_timer float64
} }
func (g *melee) setup(gg *game) { func (g *melee) setup(gg *Game) {
g.respawn_timer = 5000 g.respawn_timer = 5000
} }
func (g *melee) gameOver(gg *game) (bool, *GameOver) { func (g *melee) gameOver(gg *Game) (bool, *GameOver) {
return false, &GameOver{} return false, &GameOver{}
} }
func (g *melee) tick(gg *game, payload *Boardstate) { func (g *melee) tick(gg *Game, payload *Boardstate) {
for p := range gg.players { for p := range gg.players {
for _, r := range p.Robots { for _, r := range p.Robots {
_, ok := g.respawn[r] _, ok := g.respawn[r]

View File

@ -1,9 +1,10 @@
package main package botserv
import ( import (
v "bitbucket.org/hackerbots/vector"
"math" "math"
"math/rand" "math/rand"
v "bitbucket.org/hackerbots/vector"
) )
type Obstacle struct { type Obstacle struct {

View File

@ -1,4 +1,4 @@
package main package botserv
import ( import (
"encoding/gob" "encoding/gob"

View File

@ -1,8 +1,9 @@
package main package botserv
import ( import (
v "bitbucket.org/hackerbots/vector"
"log" "log"
v "bitbucket.org/hackerbots/vector"
) )
type Projectile struct { type Projectile struct {
@ -13,12 +14,13 @@ type Projectile struct {
Speed float32 `json:"-"` Speed float32 `json:"-"`
Damage int `json:"-"` Damage int `json:"-"`
Owner *Robot `json:"-"` Owner *Robot `json:"-"`
Delta float32
} }
func (p *Projectile) Tick(g *game) { func (p *Projectile) Tick(g *Game) {
vec := p.MoveTo.Sub(p.Position) vec := p.MoveTo.Sub(p.Position)
v_norm := vec.Normalize() v_norm := vec.Normalize()
v_scaled := v_norm.Scale(p.Speed * delta) v_scaled := v_norm.Scale(p.Speed * p.Delta)
newPos := p.Position.Add(v_scaled) newPos := p.Position.Add(v_scaled)
hit_player := false hit_player := false

View File

@ -1,4 +1,4 @@
package main package botserv
import ( import (
"log" "log"
@ -143,7 +143,7 @@ func NewFailure(reason string) *Failure {
} }
} }
func addPlayer(ws *websocket.Conn) { func (c *Controller) AddPlayer(ws *websocket.Conn) {
var gid GameID var gid GameID
err := websocket.JSON.Receive(ws, &gid) err := websocket.JSON.Receive(ws, &gid)
if err != nil { if err != nil {
@ -151,16 +151,16 @@ func addPlayer(ws *websocket.Conn) {
return return
} }
game := games.get(gid.Id) game := c.Games.get(gid.Id)
if game == nil { if game == nil {
var err error var err error
game, err = NewGame( game, err = NewGame(
gid.Id, gid.Id,
float32(conf.Width), float32(c.Conf.Width),
float32(conf.Height), float32(c.Conf.Height),
conf.Obstacles, c.Conf.Obstacles,
conf.Tick, c.Conf.Tick,
conf.MaxPoints, c.Conf.MaxPoints,
"", "",
) )
if err != nil { if err != nil {
@ -169,10 +169,10 @@ func addPlayer(ws *websocket.Conn) {
return return
} }
go game.run() go game.run()
games.add(game) c.Games.add(game)
} }
player_id := idg.Hash() player_id := c.Idg.Hash()
err = websocket.JSON.Send(ws, NewPlayerID(player_id)) err = websocket.JSON.Send(ws, NewPlayerID(player_id))
if err != nil { if err != nil {
log.Printf("game %s: unable to send player_id to player %s", gid.Id, player_id) log.Printf("game %s: unable to send player_id to player %s", gid.Id, player_id)
@ -284,7 +284,7 @@ encodingLoops:
convertedStats[name] = dstat convertedStats[name] = dstat
r := Robot{ r := Robot{
Stats: dstat, Stats: dstat,
Id: idg.Hash(), Id: c.Idg.Hash(),
Name: name, Name: name,
Health: 10, Health: 10,
Heading: v.Vector2d{1, 0}, Heading: v.Vector2d{1, 0},

View File

@ -1,10 +1,11 @@
package main package botserv
import ( import (
v "bitbucket.org/hackerbots/vector"
"log" "log"
"math" "math"
"math/rand" "math/rand"
v "bitbucket.org/hackerbots/vector"
) )
type Robot struct { type Robot struct {
@ -30,6 +31,8 @@ type Robot struct {
Probe *v.Point2d `json:"probe"` Probe *v.Point2d `json:"probe"`
ProbeResult *Collision `json:"probe_result"` ProbeResult *Collision `json:"probe_result"`
gameStats *BotStats `json:-` gameStats *BotStats `json:-`
Delta float32 `json:-`
Idg *IdGenerator
} }
type Collision struct { type Collision struct {
@ -172,7 +175,7 @@ type Instruction struct {
// returns collision, the intersection point, and the robot with whom r has // returns collision, the intersection point, and the robot with whom r has
// collided, if this happened. // collided, if this happened.
func (r *Robot) checkCollisions(g *game, probe v.Vector2d) (bool, *v.Point2d, *Robot) { func (r *Robot) checkCollisions(g *Game, probe v.Vector2d) (bool, *v.Point2d, *Robot) {
finalCollision := false finalCollision := false
collision := false collision := false
closest := float32(math.Inf(1)) closest := float32(math.Inf(1))
@ -243,19 +246,19 @@ func (r *Robot) checkCollisions(g *game, probe v.Vector2d) (bool, *v.Point2d, *R
return finalCollision, intersection, finalRobot return finalCollision, intersection, finalRobot
} }
func (r *Robot) Tick(g *game) { func (r *Robot) Tick(g *Game) {
r.Collision = nil r.Collision = nil
r.Hit = false r.Hit = false
r.scan(g) r.scan(g)
// Adjust Speed // Adjust Speed
if r.Speed < r.TargetSpeed { if r.Speed < r.TargetSpeed {
r.Speed += (r.Stats.Acceleration * delta) r.Speed += (r.Stats.Acceleration * r.Delta)
if r.Speed > r.TargetSpeed { if r.Speed > r.TargetSpeed {
r.Speed = r.TargetSpeed r.Speed = r.TargetSpeed
} }
} else if float32(math.Abs(float64(r.Speed-r.TargetSpeed))) > v.Epsilon { } else if float32(math.Abs(float64(r.Speed-r.TargetSpeed))) > v.Epsilon {
r.Speed -= (r.Stats.Acceleration * delta) r.Speed -= (r.Stats.Acceleration * r.Delta)
// Cap reverse to 1/2 speed // Cap reverse to 1/2 speed
if r.Speed < (-0.5 * r.TargetSpeed) { if r.Speed < (-0.5 * r.TargetSpeed) {
r.Speed = (-0.5 * r.TargetSpeed) r.Speed = (-0.5 * r.TargetSpeed)
@ -292,15 +295,15 @@ func (r *Robot) Tick(g *game) {
} }
// Max turn radius in this case is in degrees per second // Max turn radius in this case is in degrees per second
if float32(math.Abs(float64(angle))) > (float32(r.Stats.TurnSpeed) * delta) { if float32(math.Abs(float64(angle))) > (float32(r.Stats.TurnSpeed) * r.Delta) {
// New heading should be a little less, take current heading and // New heading should be a little less, take current heading and
// rotate by the max turn radius per frame. // rotate by the max turn radius per frame.
rot := (float32(r.Stats.TurnSpeed) * delta) * v.Deg2rad rot := (float32(r.Stats.TurnSpeed) * r.Delta) * v.Deg2rad
new_heading = current_heading.Rotate(rot * float32(dir)) new_heading = current_heading.Rotate(rot * float32(dir))
} }
move_vector := new_heading.Scale(r.Speed * delta) move_vector := new_heading.Scale(r.Speed * r.Delta)
collision, intersection_point, hit_robot := r.checkCollisions(g, move_vector) collision, intersection_point, hit_robot := r.checkCollisions(g, move_vector)
if collision { if collision {
dmg := int(math.Abs(float64(r.Speed)) / 10.0) dmg := int(math.Abs(float64(r.Speed)) / 10.0)
@ -352,7 +355,7 @@ func (r *Robot) Tick(g *game) {
// We only self repair when we're stopped // We only self repair when we're stopped
if math.Abs(float64(r.Speed)) < v.Epsilon && r.RepairCounter > 0 { if math.Abs(float64(r.Speed)) < v.Epsilon && r.RepairCounter > 0 {
r.RepairCounter -= delta r.RepairCounter -= r.Delta
if r.RepairCounter < 0 { if r.RepairCounter < 0 {
r.Health += g.repair_hp r.Health += g.repair_hp
if r.Health > r.Stats.Hp { if r.Health > r.Stats.Hp {
@ -364,9 +367,9 @@ func (r *Robot) Tick(g *game) {
// We are only allowed to scan when we're stopped // We are only allowed to scan when we're stopped
if math.Abs(float64(r.Speed)) < v.Epsilon && r.ActiveScan { if math.Abs(float64(r.Speed)) < v.Epsilon && r.ActiveScan {
r.ScanCounter += delta * float32(r.Stats.ScannerRadius) * 0.1 r.ScanCounter += r.Delta * float32(r.Stats.ScannerRadius) * 0.1
} else if r.ScanCounter > 0 { } else if r.ScanCounter > 0 {
r.ScanCounter -= delta * float32(r.Stats.ScannerRadius) * 0.05 r.ScanCounter -= r.Delta * float32(r.Stats.ScannerRadius) * 0.05
if r.ScanCounter <= 0 { if r.ScanCounter <= 0 {
r.ScanCounter = 0 r.ScanCounter = 0
} }
@ -394,7 +397,7 @@ func (r *Robot) Tick(g *game) {
} }
} }
func (r *Robot) scan(g *game) { func (r *Robot) scan(g *Game) {
r.Scanners = r.Scanners[:0] r.Scanners = r.Scanners[:0]
for player := range g.players { for player := range g.players {
for _, bot := range player.Robots { for _, bot := range player.Robots {
@ -440,9 +443,9 @@ func (r *Robot) scan(g *game) {
} }
func (r *Robot) fire(g *game) *Projectile { func (r *Robot) fire(g *Game) *Projectile {
// Throttle the fire rate // Throttle the fire rate
time_since_fired := (float32(g.turn) * (delta * 1000)) - (float32(r.LastFired) * (delta * 1000)) time_since_fired := (float32(g.turn) * (r.Delta * 1000)) - (float32(r.LastFired) * (r.Delta * 1000))
if time_since_fired < float32(r.Stats.FireRate) { if time_since_fired < float32(r.Stats.FireRate) {
return nil return nil
} }
@ -451,7 +454,7 @@ func (r *Robot) fire(g *game) *Projectile {
r.gameStats.Shots++ r.gameStats.Shots++
return &Projectile{ return &Projectile{
Id: idg.Hash(), Id: r.Idg.Hash(),
Position: r.Position, Position: r.Position,
MoveTo: *r.FireAt, MoveTo: *r.FireAt,
Damage: r.Stats.WeaponDamage, Damage: r.Stats.WeaponDamage,
@ -461,7 +464,7 @@ func (r *Robot) fire(g *game) *Projectile {
} }
} }
func (r *Robot) reset(g *game) { func (r *Robot) reset(g *Game) {
for { for {
start_pos := v.Point2d{ start_pos := v.Point2d{
X: rand.Float32() * float32(g.width), X: rand.Float32() * float32(g.width),

View File

@ -1,10 +1,11 @@
package main package botserv
import ( import (
v "bitbucket.org/hackerbots/vector"
"log" "log"
"math" "math"
"testing" "testing"
v "bitbucket.org/hackerbots/vector"
) )
func init() { func init() {

View File

@ -1,4 +1,4 @@
package main package botserv
import ( import (
v "bitbucket.org/hackerbots/vector" v "bitbucket.org/hackerbots/vector"