2013-08-19 20:43:26 -07:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
2013-09-20 11:15:10 -07:00
|
|
|
v "bitbucket.org/hackerbots/vector"
|
2013-08-19 20:43:26 -07:00
|
|
|
"log"
|
|
|
|
"sort"
|
|
|
|
"time"
|
|
|
|
)
|
|
|
|
|
2013-09-08 09:32:24 -07:00
|
|
|
const maxPlayer = 128
|
|
|
|
|
2013-09-27 22:27:05 -07:00
|
|
|
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"`
|
|
|
|
}
|
|
|
|
|
2013-08-19 20:43:26 -07:00
|
|
|
type game struct {
|
2013-09-01 23:00:09 -07:00
|
|
|
id string
|
2013-09-01 22:04:34 -07:00
|
|
|
players map[*player]bool
|
2013-09-27 22:27:05 -07:00
|
|
|
projectiles map[*Projectile]bool
|
|
|
|
splosions map[*Splosion]bool
|
2013-09-01 22:04:34 -07:00
|
|
|
register chan *player
|
|
|
|
unregister chan *player
|
|
|
|
turn int
|
|
|
|
width, height float64
|
|
|
|
spectators map[*Spectator]bool
|
|
|
|
sregister chan *Spectator
|
|
|
|
sunregister chan *Spectator
|
|
|
|
kill chan bool
|
2013-08-19 20:43:26 -07:00
|
|
|
}
|
|
|
|
|
2013-09-01 23:00:09 -07:00
|
|
|
func NewGame(id string, width, height float64) *game {
|
2013-09-04 00:06:39 -07:00
|
|
|
g := &game{
|
2013-09-01 23:00:09 -07:00
|
|
|
id: id,
|
2013-08-25 23:10:02 -07:00
|
|
|
register: make(chan *player),
|
2013-09-08 09:32:24 -07:00
|
|
|
unregister: make(chan *player, maxPlayer),
|
2013-09-27 22:27:05 -07:00
|
|
|
projectiles: make(map[*Projectile]bool),
|
|
|
|
splosions: make(map[*Splosion]bool),
|
2013-08-25 23:10:02 -07:00
|
|
|
players: make(map[*player]bool),
|
|
|
|
turn: 0,
|
2013-09-01 22:04:34 -07:00
|
|
|
width: width,
|
|
|
|
height: height,
|
2013-08-25 23:10:02 -07:00
|
|
|
spectators: make(map[*Spectator]bool),
|
|
|
|
sregister: make(chan *Spectator),
|
|
|
|
sunregister: make(chan *Spectator),
|
2013-09-08 09:32:24 -07:00
|
|
|
kill: make(chan bool, maxPlayer),
|
2013-08-25 23:10:02 -07:00
|
|
|
}
|
2013-09-04 00:06:39 -07:00
|
|
|
return g
|
|
|
|
}
|
|
|
|
|
|
|
|
func (g *game) run() {
|
2013-09-05 23:31:13 -07:00
|
|
|
var t0, t1 time.Time
|
2013-09-27 22:27:05 -07:00
|
|
|
payload := NewBoardstate()
|
2013-08-19 20:43:26 -07:00
|
|
|
for {
|
|
|
|
select {
|
2013-08-28 22:16:55 -07:00
|
|
|
case <-g.kill:
|
2013-09-04 23:23:05 -07:00
|
|
|
log.Printf("game %s: received kill signal, dying gracefully", g.id)
|
2013-09-05 18:14:41 -07:00
|
|
|
games.Lock()
|
2013-09-08 09:32:24 -07:00
|
|
|
for player := range g.players {
|
|
|
|
close(player.send)
|
|
|
|
}
|
2013-09-05 18:14:41 -07:00
|
|
|
delete(games.m, g.id)
|
|
|
|
games.Unlock()
|
2013-08-28 22:16:55 -07:00
|
|
|
return
|
2013-08-19 20:43:26 -07:00
|
|
|
case p := <-g.register:
|
|
|
|
g.players[p] = true
|
|
|
|
case p := <-g.unregister:
|
|
|
|
delete(g.players, p)
|
|
|
|
close(p.send)
|
2013-08-19 22:23:35 -07:00
|
|
|
case s := <-g.sregister:
|
|
|
|
g.spectators[s] = true
|
|
|
|
case s := <-g.sunregister:
|
|
|
|
delete(g.spectators, s)
|
|
|
|
close(s.send)
|
2013-08-19 20:43:26 -07:00
|
|
|
case <-time.Tick(time.Duration(*tick) * time.Millisecond):
|
2013-09-20 11:15:10 -07:00
|
|
|
// XXX: This is very racy!! It was at bottom of loop and empty
|
|
|
|
// stuff was going out
|
|
|
|
payload.EmptySlices()
|
2013-09-04 23:23:05 -07:00
|
|
|
|
2013-08-19 20:43:26 -07:00
|
|
|
g.turn++
|
2013-09-07 19:13:12 -07:00
|
|
|
payload.Turn = g.turn
|
|
|
|
|
2013-09-05 23:31:13 -07:00
|
|
|
t0 = time.Now()
|
2013-09-03 23:26:40 -07:00
|
|
|
|
2013-08-19 20:43:26 -07:00
|
|
|
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))
|
|
|
|
}
|
2013-09-03 23:26:40 -07:00
|
|
|
|
2013-08-19 20:43:26 -07:00
|
|
|
robots_remaining := 0
|
|
|
|
|
|
|
|
for p := range g.players {
|
|
|
|
if p.Robot.Health > 0 {
|
|
|
|
robots_remaining++
|
2013-09-20 11:15:10 -07:00
|
|
|
// TODO: measure if this would be better to go and wait ...
|
2013-09-04 00:07:47 -07:00
|
|
|
p.scan(g.players)
|
2013-09-27 01:30:07 -07:00
|
|
|
p.nudge(g)
|
2013-09-04 00:05:38 -07:00
|
|
|
if p.Robot.FireAt != nil {
|
|
|
|
proj := p.fire(g.projectiles)
|
2013-09-03 23:26:40 -07:00
|
|
|
if proj != nil {
|
|
|
|
g.projectiles[proj] = true
|
|
|
|
}
|
2013-08-19 20:43:26 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
payload.Robots = append(payload.Robots, p.Robot)
|
|
|
|
}
|
2013-09-27 22:27:05 -07:00
|
|
|
sort.Sort(RobotSorter{Robots: payload.Robots})
|
2013-08-19 20:43:26 -07:00
|
|
|
|
2013-09-20 11:15:10 -07:00
|
|
|
payload.Projectiles = append(payload.Projectiles,
|
|
|
|
g.nudgeProjectiles()...,
|
|
|
|
)
|
2013-08-19 20:43:26 -07:00
|
|
|
|
|
|
|
for s := range g.splosions {
|
2013-09-27 01:30:07 -07:00
|
|
|
s.Tick()
|
|
|
|
if !s.Alive() {
|
|
|
|
delete(g.splosions, s)
|
|
|
|
}
|
2013-08-19 20:43:26 -07:00
|
|
|
payload.Splosions = append(payload.Splosions, *s)
|
|
|
|
}
|
|
|
|
|
|
|
|
if robots_remaining <= 1 && len(g.players) > 1 {
|
|
|
|
for p := range g.players {
|
|
|
|
if p.Robot.Health > 0 {
|
|
|
|
log.Printf("Robot %v Wins", p.Robot.Id)
|
2013-08-28 23:46:12 -07:00
|
|
|
log.Printf("game over: %+v", g)
|
|
|
|
return
|
2013-08-19 20:43:26 -07:00
|
|
|
}
|
|
|
|
p.reset()
|
|
|
|
}
|
|
|
|
payload.Reset = true
|
|
|
|
} else {
|
|
|
|
payload.Reset = false
|
|
|
|
}
|
|
|
|
|
2013-09-05 23:31:13 -07:00
|
|
|
t1 = time.Now()
|
2013-08-19 20:43:26 -07:00
|
|
|
if *verbose {
|
|
|
|
log.Printf("Turn Processes %v\n", t1.Sub(t0))
|
|
|
|
}
|
|
|
|
|
|
|
|
for p := range g.players {
|
|
|
|
p.send <- payload
|
|
|
|
}
|
2013-08-19 22:23:35 -07:00
|
|
|
for s := range g.spectators {
|
|
|
|
s.send <- payload
|
|
|
|
}
|
2013-08-19 20:43:26 -07:00
|
|
|
|
|
|
|
t1 = time.Now()
|
|
|
|
if *verbose {
|
|
|
|
log.Printf("Sent Payload %v\n", t1.Sub(t0))
|
|
|
|
}
|
2013-09-20 11:15:10 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2013-08-19 20:43:26 -07:00
|
|
|
|
2013-09-27 22:27:05 -07:00
|
|
|
func (g *game) nudgeProjectiles() (rprojectiles []Projectile) {
|
|
|
|
rprojectiles = make([]Projectile, 0)
|
2013-09-20 11:15:10 -07:00
|
|
|
for p := range g.projectiles {
|
|
|
|
newPos := v.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 := v.Distance(player.Robot.Position, p.Position)
|
|
|
|
if dist < 5.0 {
|
|
|
|
hit_player = true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if v.Distance(p.Position, p.MoveTo) < 5 || hit_player {
|
|
|
|
delete(g.projectiles, p)
|
|
|
|
|
|
|
|
// Spawn a splosion
|
2013-09-27 22:27:05 -07:00
|
|
|
splo := &Splosion{
|
2013-09-20 11:15:10 -07:00
|
|
|
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 := v.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 {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2013-08-19 20:43:26 -07:00
|
|
|
}
|
2013-09-20 11:15:10 -07:00
|
|
|
p.Position.X = newPos.X
|
|
|
|
p.Position.Y = newPos.Y
|
2013-09-27 01:30:07 -07:00
|
|
|
rprojectiles = append(rprojectiles, *p)
|
2013-08-19 20:43:26 -07:00
|
|
|
}
|
2013-09-27 01:30:07 -07:00
|
|
|
|
2013-09-20 11:15:10 -07:00
|
|
|
return
|
2013-08-19 20:43:26 -07:00
|
|
|
}
|