From b074dda6771f7bc52059564ee819e2f8a5e55748 Mon Sep 17 00:00:00 2001 From: Stephen McQuay Date: Sun, 4 May 2014 00:33:46 -0700 Subject: [PATCH] Added curses (termbox) spectator mode --- {bspect => botspectate}/main.go | 13 +++- spectator.go | 131 +++++++++++++++++++++++++++++++- 2 files changed, 138 insertions(+), 6 deletions(-) rename {bspect => botspectate}/main.go (85%) diff --git a/bspect/main.go b/botspectate/main.go similarity index 85% rename from bspect/main.go rename to botspectate/main.go index 27fec05..be01f5e 100644 --- a/bspect/main.go +++ b/botspectate/main.go @@ -39,12 +39,17 @@ func main() { log.Fatalf("%s: failed to negociate: %s", c.Name, err) } - c.Player = client.NewSimpleSpectator( + spectator := client.NewSimpleSpectator( c.Game.BoardSize.Width, c.Game.BoardSize.Height, ) + c.Player = spectator - if err := c.Play(); err != nil { - log.Fatal(err) - } + go func() { + if err := c.Play(); err != nil { + log.Fatal(err) + } + }() + + spectator.Run() } diff --git a/spectator.go b/spectator.go index 0b3b5c8..1d7d22b 100644 --- a/spectator.go +++ b/spectator.go @@ -3,11 +3,26 @@ package client import ( "log" + "github.com/nsf/termbox-go" + "bitbucket.org/hackerbots/server" ) +var bots = []rune{ + '。', 'ヲ', 'ァ', 'ィ', 'ゥ', 'ェ', 'ォ', 'ャ', 'ュ', 'ョ', 'ッ', 'ー', 'ア', 'イ', 'ウ', + 'エ', 'オ', 'カ', 'キ', 'ク', 'ケ', 'コ', 'サ', 'シ', 'ス', 'セ', 'ソ', 'タ', 'チ', 'ツ', + 'テ', 'ト', 'ナ', 'ニ', 'ヌ', 'ネ', 'ノ', 'ハ', 'ヒ', 'フ', 'ヘ', 'ホ', 'マ', 'ミ', 'ム', + 'メ', 'モ', 'ヤ', 'ユ', 'ヨ', 'ラ', 'リ', 'ル', 'レ', 'ロ', 'ワ', 'ン', '゙', '゚', +} + +type size struct { + width, height int +} + type SimpleSpectator struct { width, height float64 + viewX, viewY int + update chan *server.Boardstate } // NewSimpleSpectator simply returns a populated, usable *SimplePlayer @@ -15,11 +30,123 @@ func NewSimpleSpectator(width, height float64) *SimpleSpectator { return &SimpleSpectator{ width: width, height: height, + update: make(chan *server.Boardstate), } } // Recv is our implementation of receiving a server.Boardstate from the server -func (p *SimpleSpectator) Update(bs *server.Boardstate) map[string]server.Instruction { - log.Println("hi") +func (s *SimpleSpectator) Update(bs *server.Boardstate) map[string]server.Instruction { + s.update <- bs return nil } + +func (s *SimpleSpectator) Run() { + err := termbox.Init() + if err != nil { + log.Fatal(err) + } + + s.viewX, s.viewY = termbox.Size() + + events := make(chan termbox.Event) + go func() { + for { + events <- termbox.PollEvent() + } + }() + + termbox.HideCursor() + termbox.Clear(termbox.ColorBlack, termbox.ColorBlack) + + func() { + for { + select { + case event := <-events: + switch event.Type { + case termbox.EventKey: + switch event.Key { + case termbox.KeyCtrlZ, termbox.KeyCtrlC: + log.Printf("exiting ...") + return + } + + switch event.Ch { + case 'q': + return + case 'c': + termbox.Clear(termbox.ColorBlack, termbox.ColorBlack) + } + + case termbox.EventResize: + s.viewX, s.viewY = event.Width, event.Height + case termbox.EventError: + log.Fatalf("Quitting because of termbox error: \n%s\n", event.Err) + } + case u := <-s.update: + termbox.Clear(termbox.ColorBlack, termbox.ColorBlack) + + for _, obstacle := range u.Obstacles { + startX := int((obstacle.Bounds.A.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)) + stopY := int((obstacle.Bounds.B.Y / s.height) * float64(s.viewY)) + for x := startX; x < stopX; x++ { + for y := startY; y < stopY; y++ { + termbox.SetCell( + x, + s.viewY-y, + ' ', + termbox.ColorBlack, termbox.ColorBlue, + ) + } + } + } + + for i, bot := range u.OtherRobots { + x := int((bot.Position.X / s.width) * float64(s.viewX)) + y := int((bot.Position.Y / s.height) * float64(s.viewY)) + b := bots[i%len(bots)] + c := termbox.ColorWhite + if bot.Health < 0 { + c = termbox.ColorBlack + } + termbox.SetCell( + x, + s.viewY-y, + b, + c|termbox.AttrBold, termbox.ColorBlack, + ) + } + + for _, p := range u.Projectiles { + x := int((p.Position.X / s.width) * float64(s.viewX)) + y := int((p.Position.Y / s.height) * float64(s.viewY)) + termbox.SetCell( + x, + s.viewY-y, + '·', + termbox.ColorWhite|termbox.AttrBold, termbox.ColorBlack, + ) + } + + for _, splosion := range u.Splosions { + x := int((splosion.Position.X / s.width) * float64(s.viewX)) + y := int((splosion.Position.Y / s.height) * float64(s.viewY)) + termbox.SetCell( + x, + s.viewY-y, + 'O', + termbox.ColorRed|termbox.AttrBold, termbox.ColorBlack, + ) + } + + err := termbox.Flush() + if err != nil { + log.Fatal(err) + } + } + } + }() + + termbox.Close() +}