Made Spectator/term ui pluggable
This commit is contained in:
parent
f9984c1826
commit
f1ae71ecc0
18
client.go
18
client.go
@ -27,17 +27,14 @@ type Client struct {
|
|||||||
Server string
|
Server string
|
||||||
Game server.GameParam
|
Game server.GameParam
|
||||||
|
|
||||||
|
// max dimensions of field
|
||||||
|
Width, Height float64
|
||||||
|
|
||||||
boardstate *server.Boardstate
|
boardstate *server.Boardstate
|
||||||
enc encoder
|
enc encoder
|
||||||
dec decoder
|
dec decoder
|
||||||
ws *websocket.Conn
|
ws *websocket.Conn
|
||||||
|
|
||||||
// visualization members
|
|
||||||
width, height float64
|
|
||||||
viewX, viewY int
|
|
||||||
StateStream chan *server.Boardstate
|
|
||||||
Die chan struct{}
|
|
||||||
|
|
||||||
Player
|
Player
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -149,8 +146,8 @@ func (c *Client) Negotiate(clientType string, player Player) (err error) {
|
|||||||
case "spectator":
|
case "spectator":
|
||||||
}
|
}
|
||||||
|
|
||||||
c.width = c.Game.BoardSize.Width
|
c.Width = c.Game.BoardSize.Width
|
||||||
c.height = c.Game.BoardSize.Height
|
c.Height = c.Game.BoardSize.Height
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -167,11 +164,6 @@ func (c *Client) Play() error {
|
|||||||
return errors.New(fmt.Sprintf("%s: Connection likely lost: %s", c.Name, err))
|
return errors.New(fmt.Sprintf("%s: Connection likely lost: %s", c.Name, err))
|
||||||
}
|
}
|
||||||
|
|
||||||
select {
|
|
||||||
case c.StateStream <- bs:
|
|
||||||
default:
|
|
||||||
}
|
|
||||||
|
|
||||||
err = c.enc.Encode(c.Update(bs))
|
err = c.enc.Encode(c.Update(bs))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -8,7 +8,6 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"hackerbots.us/client"
|
"hackerbots.us/client"
|
||||||
"hackerbots.us/server"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var addr = flag.String("addr", "ws://localhost:8666", "server hostname")
|
var addr = flag.String("addr", "ws://localhost:8666", "server hostname")
|
||||||
@ -16,22 +15,19 @@ var forceJSON = flag.Bool("json", false, "force json encoding")
|
|||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
rand.Seed(time.Now().UnixNano())
|
rand.Seed(time.Now().UnixNano())
|
||||||
var gameId string
|
var gameID string
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
if flag.NArg() < 1 {
|
if flag.NArg() < 1 {
|
||||||
gameId = "debug"
|
gameID = "debug"
|
||||||
} else {
|
} else {
|
||||||
gameId = flag.Arg(0)
|
gameID = flag.Arg(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
c := &client.Client{
|
c := &client.Client{
|
||||||
Server: *addr,
|
Server: *addr,
|
||||||
Name: "bspect",
|
Name: "bspect",
|
||||||
GameId: gameId,
|
GameId: gameID,
|
||||||
ForceJSON: *forceJSON,
|
ForceJSON: *forceJSON,
|
||||||
StateStream: make(chan *server.Boardstate),
|
|
||||||
Die: make(chan struct{}),
|
|
||||||
Player: client.Spectator{},
|
|
||||||
}
|
}
|
||||||
var err error
|
var err error
|
||||||
err = c.Negotiate("spectator", c.Player)
|
err = c.Negotiate("spectator", c.Player)
|
||||||
@ -40,10 +36,16 @@ func main() {
|
|||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ui := client.NewSpectator(c.Width, c.Height)
|
||||||
|
c.Player = ui
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
if err := c.Play(); err != nil {
|
if err := c.Play(); err != nil {
|
||||||
close(c.Die)
|
close(ui.Die)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
c.Visualize()
|
if err := ui.Spectate(); err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "problem during visualization: %+v\n", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -26,7 +26,7 @@ var weaponSpeed = flag.Int("wspeed", 50, "weapons speed")
|
|||||||
var addr = flag.String("addr", "ws://localhost:8666", "server hostname")
|
var addr = flag.String("addr", "ws://localhost:8666", "server hostname")
|
||||||
var botname = flag.String("name", "gobot", "the name that other players will see")
|
var botname = flag.String("name", "gobot", "the name that other players will see")
|
||||||
var forceJSON = flag.Bool("json", false, "force json encoding")
|
var forceJSON = flag.Bool("json", false, "force json encoding")
|
||||||
var botType = flag.String("bot", "simple", "which Bot")
|
var botType = flag.String("bot", "simple", "which Bot [fraserbot, simple]")
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
rand.Seed(time.Now().UnixNano())
|
rand.Seed(time.Now().UnixNano())
|
||||||
@ -39,13 +39,10 @@ func main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
c := &client.Client{
|
c := &client.Client{
|
||||||
Server: *addr,
|
Server: *addr,
|
||||||
Name: *botname,
|
Name: *botname,
|
||||||
GameId: gameId,
|
GameId: gameId,
|
||||||
// XXX: update with missing fields
|
ForceJSON: *forceJSON,
|
||||||
ForceJSON: *forceJSON,
|
|
||||||
StateStream: make(chan *server.Boardstate),
|
|
||||||
Die: make(chan struct{}),
|
|
||||||
}
|
}
|
||||||
sr := server.StatsRequest{
|
sr := server.StatsRequest{
|
||||||
Hp: *hp,
|
Hp: *hp,
|
||||||
@ -75,11 +72,8 @@ func main() {
|
|||||||
fmt.Fprintf(os.Stderr, "%s: failed to negociate: %s\n", c.Name, err)
|
fmt.Fprintf(os.Stderr, "%s: failed to negociate: %s\n", c.Name, err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
if err := c.Play(); err != nil {
|
||||||
go func() {
|
fmt.Fprintf(os.Stderr, "problem during play: %v\n", err)
|
||||||
if err := c.Play(); err != nil {
|
os.Exit(1)
|
||||||
close(c.Die)
|
}
|
||||||
}
|
|
||||||
}()
|
|
||||||
c.Visualize()
|
|
||||||
}
|
}
|
||||||
|
15
player.go
15
player.go
@ -2,8 +2,7 @@ package client
|
|||||||
|
|
||||||
import "hackerbots.us/server"
|
import "hackerbots.us/server"
|
||||||
|
|
||||||
// Player is the interface that is implemented when specifying non-default
|
// Player is the interface that defines a player's behavior.
|
||||||
// player behavior.
|
|
||||||
//
|
//
|
||||||
// The general case will be to implement a Player type that contains the magic
|
// The general case will be to implement a Player type that contains the magic
|
||||||
// required to slay other robots quickly while staying alive for a long time.
|
// required to slay other robots quickly while staying alive for a long time.
|
||||||
@ -24,15 +23,3 @@ type Player interface {
|
|||||||
// the instructions for each robot in a map of robot id to instructions
|
// the instructions for each robot in a map of robot id to instructions
|
||||||
Update(bs *server.Boardstate) map[string]server.Instruction
|
Update(bs *server.Boardstate) map[string]server.Instruction
|
||||||
}
|
}
|
||||||
|
|
||||||
type Spectator struct{}
|
|
||||||
|
|
||||||
func (s Spectator) SetIDs(map[string]string) {}
|
|
||||||
|
|
||||||
func (s Spectator) GetStats() map[string]server.StatsRequest {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s Spectator) Update(bs *server.Boardstate) map[string]server.Instruction {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
97
spectator.go
97
spectator.go
@ -1,33 +1,61 @@
|
|||||||
package client
|
package client
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math"
|
"math"
|
||||||
|
|
||||||
"github.com/nsf/termbox-go"
|
|
||||||
|
|
||||||
"hackerbots.us/server"
|
"hackerbots.us/server"
|
||||||
|
|
||||||
|
"github.com/nsf/termbox-go"
|
||||||
)
|
)
|
||||||
|
|
||||||
var botDown rune = 'v'
|
const d = 'v'
|
||||||
var botUp rune = '^'
|
const u = '^'
|
||||||
var botRight rune = '>'
|
const r = '>'
|
||||||
var botLeft rune = '<'
|
const l = '<'
|
||||||
|
|
||||||
type size struct {
|
// NewSpectator initializes and returns a *Spectator.
|
||||||
width, height int
|
func NewSpectator(w, h float64) *Spectator {
|
||||||
|
return &Spectator{
|
||||||
|
StateStream: make(chan *server.Boardstate),
|
||||||
|
Die: make(chan struct{}),
|
||||||
|
width: w,
|
||||||
|
height: h,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Recv is our implementation of receiving a server.Boardstate from the server
|
// Spectator encodes a termbox ui.
|
||||||
func (s *Client) Recv(bs *server.Boardstate) map[string]server.Instruction {
|
type Spectator struct {
|
||||||
|
// max dimensions of field
|
||||||
|
width, height float64
|
||||||
|
|
||||||
|
// dimensions of the terminal window
|
||||||
|
viewX, viewY int
|
||||||
|
|
||||||
|
StateStream chan *server.Boardstate
|
||||||
|
|
||||||
|
// when closed will cause the Spectator to exit the render loop.
|
||||||
|
Die chan struct{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetIDs is implemented so Spectator can be used as a client.Player.
|
||||||
|
func (s Spectator) SetIDs(map[string]string) {}
|
||||||
|
|
||||||
|
// GetStats is implemented so Spectator can be used as a client.Player.
|
||||||
|
func (s Spectator) GetStats() map[string]server.StatsRequest { return nil }
|
||||||
|
|
||||||
|
// Update is implemented so Spectator can be used as a client.Player.
|
||||||
|
func (s Spectator) Update(bs *server.Boardstate) map[string]server.Instruction {
|
||||||
s.StateStream <- bs
|
s.StateStream <- bs
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Client) Visualize() {
|
// Spectate runs the termbox render loop.
|
||||||
|
func (s *Spectator) Spectate() error {
|
||||||
err := termbox.Init()
|
err := termbox.Init()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
s.viewX, s.viewY = termbox.Size()
|
s.viewX, s.viewY = termbox.Size()
|
||||||
@ -56,6 +84,13 @@ func (s *Client) Visualize() {
|
|||||||
switch event.Ch {
|
switch event.Ch {
|
||||||
case 'q':
|
case 'q':
|
||||||
return
|
return
|
||||||
|
case 'f':
|
||||||
|
termbox.SetCell(
|
||||||
|
20,
|
||||||
|
20,
|
||||||
|
'*',
|
||||||
|
termbox.ColorRed, termbox.ColorBlack,
|
||||||
|
)
|
||||||
case 'c':
|
case 'c':
|
||||||
termbox.Clear(termbox.ColorBlack, termbox.ColorBlack)
|
termbox.Clear(termbox.ColorBlack, termbox.ColorBlack)
|
||||||
}
|
}
|
||||||
@ -63,12 +98,13 @@ func (s *Client) Visualize() {
|
|||||||
case termbox.EventResize:
|
case termbox.EventResize:
|
||||||
s.viewX, s.viewY = event.Width, event.Height
|
s.viewX, s.viewY = event.Width, event.Height
|
||||||
case termbox.EventError:
|
case termbox.EventError:
|
||||||
panic(fmt.Sprintf("Quitting because of termbox error:\n%v\n", event.Err))
|
err = fmt.Errorf("Quitting because of termbox error:\n%v\n", event.Err)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
case u := <-s.StateStream:
|
case update := <-s.StateStream:
|
||||||
termbox.Clear(termbox.ColorBlack, termbox.ColorBlack)
|
termbox.Clear(termbox.ColorBlack, termbox.ColorBlack)
|
||||||
|
|
||||||
for _, obstacle := range u.Obstacles {
|
for _, obstacle := range update.Obstacles {
|
||||||
startX := int((obstacle.Bounds.A.X / s.width) * float64(s.viewX))
|
startX := int((obstacle.Bounds.A.X / s.width) * float64(s.viewX))
|
||||||
stopX := int((obstacle.Bounds.B.X / s.width) * float64(s.viewX))
|
stopX := int((obstacle.Bounds.B.X / s.width) * float64(s.viewX))
|
||||||
startY := int((obstacle.Bounds.A.Y / s.height) * float64(s.viewY))
|
startY := int((obstacle.Bounds.A.Y / s.height) * float64(s.viewY))
|
||||||
@ -85,21 +121,21 @@ func (s *Client) Visualize() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, bot := range u.OtherRobots {
|
for _, bot := range update.OtherRobots {
|
||||||
x := int((bot.Position.X / s.width) * float64(s.viewX))
|
x := int((bot.Position.X / s.width) * float64(s.viewX))
|
||||||
y := int((bot.Position.Y / s.height) * float64(s.viewY))
|
y := int((bot.Position.Y / s.height) * float64(s.viewY))
|
||||||
var b rune
|
var b rune
|
||||||
if math.Abs(bot.Heading.X) > math.Abs(bot.Heading.Y) {
|
if math.Abs(bot.Heading.X) > math.Abs(bot.Heading.Y) {
|
||||||
if bot.Heading.X > 0 {
|
if bot.Heading.X > 0 {
|
||||||
b = botRight
|
b = r
|
||||||
} else {
|
} else {
|
||||||
b = botLeft
|
b = l
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if bot.Heading.Y > 0 {
|
if bot.Heading.Y > 0 {
|
||||||
b = botUp
|
b = u
|
||||||
} else {
|
} else {
|
||||||
b = botDown
|
b = d
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
c := termbox.ColorRed
|
c := termbox.ColorRed
|
||||||
@ -114,7 +150,7 @@ func (s *Client) Visualize() {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, p := range u.Projectiles {
|
for _, p := range update.Projectiles {
|
||||||
x := int((p.Position.X / s.width) * float64(s.viewX))
|
x := int((p.Position.X / s.width) * float64(s.viewX))
|
||||||
y := int((p.Position.Y / s.height) * float64(s.viewY))
|
y := int((p.Position.Y / s.height) * float64(s.viewY))
|
||||||
termbox.SetCell(
|
termbox.SetCell(
|
||||||
@ -125,7 +161,7 @@ func (s *Client) Visualize() {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, splosion := range u.Splosions {
|
for _, splosion := range update.Splosions {
|
||||||
startX := int(((splosion.Position.X - float64(splosion.Radius)) / s.width) * float64(s.viewX))
|
startX := int(((splosion.Position.X - float64(splosion.Radius)) / s.width) * float64(s.viewX))
|
||||||
startY := int(((splosion.Position.Y - float64(splosion.Radius)) / s.height) * float64(s.viewY))
|
startY := int(((splosion.Position.Y - float64(splosion.Radius)) / s.height) * float64(s.viewY))
|
||||||
stopX := int(((splosion.Position.X+float64(splosion.Radius))/s.width)*float64(s.viewX)) + 1
|
stopX := int(((splosion.Position.X+float64(splosion.Radius))/s.width)*float64(s.viewX)) + 1
|
||||||
@ -149,21 +185,21 @@ func (s *Client) Visualize() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, bot := range u.MyRobots {
|
for _, bot := range update.MyRobots {
|
||||||
x := int((bot.Position.X / s.width) * float64(s.viewX))
|
x := int((bot.Position.X / s.width) * float64(s.viewX))
|
||||||
y := int((bot.Position.Y / s.height) * float64(s.viewY))
|
y := int((bot.Position.Y / s.height) * float64(s.viewY))
|
||||||
var b rune
|
var b rune
|
||||||
if math.Abs(bot.Heading.X) > math.Abs(bot.Heading.Y) {
|
if math.Abs(bot.Heading.X) > math.Abs(bot.Heading.Y) {
|
||||||
if bot.Heading.X > 0 {
|
if bot.Heading.X > 0 {
|
||||||
b = botRight
|
b = r
|
||||||
} else {
|
} else {
|
||||||
b = botLeft
|
b = l
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if bot.Heading.Y > 0 {
|
if bot.Heading.Y > 0 {
|
||||||
b = botUp
|
b = u
|
||||||
} else {
|
} else {
|
||||||
b = botDown
|
b = d
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
c := termbox.ColorWhite
|
c := termbox.ColorWhite
|
||||||
@ -178,15 +214,14 @@ func (s *Client) Visualize() {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
err := termbox.Flush()
|
err = termbox.Flush()
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
case <-s.Die:
|
case <-s.Die:
|
||||||
|
err = errors.New("was told to die")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
termbox.Close()
|
termbox.Close()
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user