diff --git a/gobot/main.go b/gobot/main.go index 4a37936..8d81ff8 100644 --- a/gobot/main.go +++ b/gobot/main.go @@ -30,6 +30,7 @@ var forceJSON = flag.Bool("json", false, "force json encoding") func main() { log.SetFlags(log.Ldate | log.Ltime | log.Lshortfile) + rand.Seed(time.Now().UnixNano()) var gameId string flag.Parse() @@ -39,6 +40,7 @@ func main() { gameId = flag.Arg(0) } + client.Verbose = *verbose c := &client.Client{ Server: *serverHostname, Port: *port, diff --git a/player.go b/player.go index f06580d..f72a729 100644 --- a/player.go +++ b/player.go @@ -9,6 +9,8 @@ import ( "bitbucket.org/hackerbots/vector" ) +var Verbose bool = false + // Player is the interface that is implemented when specifying non-default // player behavior. // @@ -22,90 +24,120 @@ type Player interface { // own Player implementations. type SimplePlayer struct { me server.Robot - width, height float32 + width, height float64 knownObstacles map[string]server.Obstacle nearestEnemy *server.OtherRobot fireat *vector.Point2d moveto *vector.Point2d - speed float32 - maxSpeed float32 - safeDistance float32 + speed float64 + maxSpeed float64 + safeDistance float64 } // NewSimplePlayer simply returns a populated, usable *SimplePlayer -func NewSimplePlayer(width, height float32) *SimplePlayer { +func NewSimplePlayer(width, height float64) *SimplePlayer { return &SimplePlayer{ knownObstacles: make(map[string]server.Obstacle), width: width, height: height, - maxSpeed: 100, - safeDistance: 40, + maxSpeed: 1000, + safeDistance: 50, } } // Recv is our implementation of receiving a server.Boardstate from the server func (p *SimplePlayer) Update(bot *server.Robot, bs *server.Boardstate) server.Instruction{ - instruction := server.Instruction{ - MoveTo: nil, - TargetSpeed: nil, - FireAt: nil, - } - - p.speed = p.maxSpeed - if len(bs.MyRobots) > 0 { - p.me = bs.MyRobots[0] - } else { - return instruction + p.me = *bot + p.speed = 1000 + if p.me.Health <= 0{ + return server.Instruction{} } p.recon(bs) p.navigate() + + probe_point := p.me.Position.Add(p.me.Heading.Scale(p.safeDistance)) + + if Verbose { + fmt.Printf("PROBE SENT: %v\n",probe_point) + } return server.Instruction{ MoveTo: p.moveto, TargetSpeed: &p.speed, FireAt: p.fireat, + Probe: &probe_point, } } func (p *SimplePlayer) navigate() { + if Verbose { + fmt.Printf("%v S:%v H:%v TS:%v\n\tX:%v Y:%v\n\tHX:%v HY:%v\n", + p.me.Name, p.me.Speed, p.me.Health, p.me.TargetSpeed, + p.me.Position.X, p.me.Position.Y, + p.me.Heading.X, p.me.Heading.Y) + + if p.me.MoveTo != nil { + fmt.Printf("\tTX:%v TY:%v\n", + p.me.MoveTo.X, p.me.MoveTo.Y) + } + } + + // if !p.probe(p.me.Position.Add(p.me.Heading.Scale(p.safeDistance))) { + // if !p.probe(*p.moveto) { + // p.moveto = p.randomDirectionDrift(p.moveto, 20) + // // p.speed = p.maxSpeed + // fmt.Printf("Obstacle?\n") + // return + // } + // } + + if p.me.ProbeResult != nil { + p.moveto = p.randomDirectionDrift(&p.me.Position, 100) + p.speed = -20 + if Verbose { + fmt.Printf("Probe %v\n", p.me.ProbeResult) + } + return + } + + if p.me.Collision != nil { + p.moveto = p.randomDirectionDrift(&p.me.Position, 100) + p.speed = -20 + if Verbose { + fmt.Printf("Hit!\n") + } + return + } + if p.moveto == nil { p.moveto = p.randomDirection() + // p.speed = p.maxSpeed + if Verbose { + fmt.Printf("Start\n") + } + return } togo := p.me.Position.Sub(*p.moveto).Mag() if togo < p.safeDistance+5 { p.moveto = p.randomDirection() - return - } - if !p.probe(p.me.Position.Add(p.me.Heading.Scale(p.safeDistance))) { - p.speed = 0 - if !p.probe(*p.moveto) { - p.moveto = p.randomDirection() - return + // p.speed = p.maxSpeed + if Verbose { + fmt.Printf("New Dest\n") } - } - if p.me.Collision != nil { - p.moveto = p.randomDirection() - p.speed = 0 return } } func (p *SimplePlayer) recon(bs *server.Boardstate) { - for _, o := range bs.Objects { - obj := MiniObstacle(o) - if _, ok := p.knownObstacles[obj.Id()]; !ok { - p.knownObstacles[obj.Id()] = obj.ToObstacle() - } - } // simplest shooting strategy ... need to do the following: // not shoot through buildings // shoot at where the robot will be, not where it was. p.nearestEnemy = nil p.fireat = nil - closest := float32(math.Inf(1)) + closest := math.Inf(1) for _, enemy := range bs.OtherRobots { dist := p.me.Position.Sub(enemy.Position).Mag() if dist < closest && dist > p.safeDistance { @@ -118,10 +150,23 @@ func (p *SimplePlayer) recon(bs *server.Boardstate) { } } +func (p *SimplePlayer) randomDirectionDrift(start *vector.Point2d, drift float64) *vector.Point2d { + for { + pt := vector.Vector2d{ + X: start.X - drift + rand.Float64() * drift * 2, + Y: start.Y - drift + rand.Float64() * drift * 2, + }.ToPoint() + + if pt.X > 0 && pt.X < p.width && pt.Y > 0 && pt.Y < p.height { + return &pt + } + } +} + func (p *SimplePlayer) randomDirection() *vector.Point2d { pt := vector.Vector2d{ - X: rand.Float32() * p.width, - Y: rand.Float32() * p.height, + X: rand.Float64() * p.width, + Y: rand.Float64() * p.height, }.ToPoint() return &pt } @@ -140,31 +185,3 @@ func (p *SimplePlayer) probe(destination vector.Point2d) bool { } return true } - -// MiniObstacle is a convenient way to encode/decode between the [4]int -> server.Obstacle -type MiniObstacle [4]int - -// id is used to calculate a key for use in maps -func (mo *MiniObstacle) Id() string { - return fmt.Sprintf( - "%x%x%x%x", - mo[0], - mo[1], - mo[2], - mo[3], - ) -} - -func (mo MiniObstacle) String() string { - return mo.Id() -} - -// ToObstacle is where the conversion magic happens -func (mo *MiniObstacle) ToObstacle() server.Obstacle { - return server.Obstacle{ - Bounds: vector.AABB2d{ - A: vector.Point2d{X: float32(mo[0]), Y: float32(mo[1])}, - B: vector.Point2d{X: float32(mo[2]), Y: float32(mo[3])}, - }, - } -}