Skip to content

Commit

Permalink
Add Test Engine (#3)
Browse files Browse the repository at this point in the history
* add test setup and game engine

* set debug to false during tests

* fix nil pointer bug, rename rpc
  • Loading branch information
imw authored Feb 26, 2022
1 parent 9003afa commit ef9cb8d
Show file tree
Hide file tree
Showing 5 changed files with 186 additions and 55 deletions.
3 changes: 2 additions & 1 deletion board/square.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,9 @@ func (s *Square) Name() string {
func (s *Square) SetOccupant(p *Piece) {
if p == nil {
s.occupant = nil
} else {
s.occupant = *p
}
s.occupant = *p
}

//Occupant returns an occupying piece, if any
Expand Down
15 changes: 3 additions & 12 deletions display/display.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package display

import (
"0x539.lol/rtc/board"
"0x539.lol/rtc/game"
"0x539.lol/rtc/util"
"github.com/gdamore/tcell/v2"
)
Expand All @@ -27,17 +28,6 @@ func Loading(s tcell.Screen) {
s.Show()
}

//Greeting displays a ready greeting
func Greeting(s tcell.Screen) {
w, h := s.Size()
s.Clear()
style := tcell.StyleDefault.Foreground(tcell.ColorCadetBlue.TrueColor()).Background(tcell.ColorWhite)
drawText(s, w/2-8, h/2, w/2+8, h/2, style, "REAL TIME CHESS")
drawText(s, w/2-9, h/2+1, w/2+9, h/2+1, tcell.StyleDefault, "Press ESC to exit.")
drawText(s, w/2-12, h/2+2, w/2+12, h/2+2, tcell.StyleDefault, "Press any key to begin.")
s.Show()
}

func drawText(s tcell.Screen, x1, y1, x2, y2 int, style tcell.Style, text string) {
row := y1
col := x1
Expand Down Expand Up @@ -76,7 +66,8 @@ func drawSquare(s tcell.Screen, x1, y1, x2, y2 int, boardStyle tcell.Style, piec
}

//Render updates a board drawing from a game board
func Render(b *board.Board, s tcell.Screen) {
func Render(g *game.Game, s tcell.Screen) {
b := g.GetBoard()
s.Clear()
defStyle := tcell.StyleDefault.Background(tcell.ColorReset).Foreground(tcell.ColorReset)
s.SetStyle(defStyle)
Expand Down
22 changes: 16 additions & 6 deletions game/game.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (

"0x539.lol/rtc/board"
"0x539.lol/rtc/util"
"github.com/gdamore/tcell/v2"
)

//Game encapsulates data about a game session
Expand All @@ -17,23 +18,32 @@ type Game struct {
}

//New returns a new Game struct and GameRPC client
func New(b *board.Board) (*Game, *RPC) {
func New(b *board.Board) (*Game, *GameRPC) {
g := &Game{
board: b,
}
gr := &RPC{
gr := &GameRPC{
game: g,
}
return g, gr
}

func (g *Game) GetBoard() *board.Board {
return g.board
}

func (g *Game) RecieveEvent(ev *tcell.EventKey) board.Move {
b := g.GetBoard()
return b.ProcessEvent(ev)
}

//SetClient updates the rpc client for a game
func (g *Game) SetClient(c *rpc.Client) {
g.client = c
}

//RPC is an RPC wrapper for the Game structure
type RPC struct {
//GameRPC is an RPC wrapper for the Game structure
type GameRPC struct {
game *Game
}

Expand All @@ -54,7 +64,7 @@ func (g *Game) Peered() bool {
}

//DoMove executes a move against a local game board
func (gr *RPC) DoMove(m board.Move, exit *int) error {
func (gr *GameRPC) DoMove(m board.Move, exit *int) error {
util.Write(fmt.Sprintf("Local move: %v", m))
loc := gr.game.board.Position(m.Loc)
tgt := gr.game.board.Position(m.Tgt)
Expand All @@ -75,7 +85,7 @@ func (gr *RPC) DoMove(m board.Move, exit *int) error {
}

//Register updates the peerID for a game session
func (gr *RPC) Register(clientID string, exit *int) error {
func (gr *GameRPC) Register(clientID string, exit *int) error {
gr.game.peerID = clientID
return nil
}
113 changes: 77 additions & 36 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,17 +29,31 @@ type Config struct {
Debug bool `default:"false"`
}

func main() {
type GameEngine struct {
ID string
game *game.Game
rpc *game.GameRPC
listener net.Listener
}

type ExitCode int

const (
Success ExitCode = iota
Failure
)

func main() {
var c Config
err := envconfig.Process("rtc", &c)
if err != nil {
log.Fatal(err.Error())
}

util.Debug = c.Debug
os.Exit(int(entrypoint(c)))
}

util.Write(fmt.Sprintf("Config: %v", c))
func setup(c Config) (GameEngine, error) {

var b *board.Board

Expand All @@ -66,62 +80,87 @@ func main() {

//register RPC
rpc.Register(gr)

mux := http.NewServeMux()
http.DefaultServeMux = mux
rpc.HandleHTTP()

l, e := net.Listen("tcp", localhost+":"+localport)
if e != nil {
log.Fatal("listen error:", e)
}
go http.Serve(l, nil)
defer l.Close()
util.Write(fmt.Sprintf("Listening with listener: %v", l))

dialCount := 1
for {
//init client for peer
util.Write(fmt.Sprintf("Dial #%d", dialCount))
client, e := rpc.DialHTTP("tcp", remotehost+":"+remoteport)
if e != nil {
util.Write(fmt.Sprintf("error attempting to dial %s:%s: %s", remotehost, remoteport, e))
if dialCount > 10 {
return GameEngine{}, e
}
} else {
g.SetClient(client)
break
}
dialCount = dialCount + 1
/*
switch ev := s.PollEvent().(type) {
case *tcell.EventKey:
if ev.Key() == tcell.KeyEscape {
s.Fini()
return Success
}
}
*/
time.Sleep(time.Duration(1) * time.Second)
}

return GameEngine{c.ID, g, gr, l}, nil

}

func entrypoint(c Config) ExitCode {

util.Debug = c.Debug
util.Write(fmt.Sprintf("Config: %v", c))

//setup display
encoding.Register()
s, e := tcell.NewScreen()
if e != nil {
fmt.Fprintf(os.Stderr, "%v\n", e)
os.Exit(1)
return Failure
}

if e := s.Init(); e != nil {
fmt.Fprintf(os.Stderr, "%v\n", e)
os.Exit(1)
return Failure
}

defStyle := tcell.StyleDefault.
Background(tcell.ColorBlack).
Foreground(tcell.ColorWhite)
s.SetStyle(defStyle)

display.Loading(s)

dialCount := 1
for {
//init client for peer
util.Write(fmt.Sprintf("Dial #%d", dialCount))
client, err := rpc.DialHTTP("tcp", remotehost+":"+remoteport)
if err != nil {
util.Write(fmt.Sprintf("error attempting to dial %s:%s: %s", remotehost, remoteport, err))
} else {
g.SetClient(client)
break
}
dialCount = dialCount + 1
switch ev := s.PollEvent().(type) {
case *tcell.EventKey:
if ev.Key() == tcell.KeyEscape {
s.Fini()
os.Exit(0)
}
}
time.Sleep(time.Duration(1) * time.Second)
engine, e := setup(c)
if e != nil {
fmt.Fprintf(os.Stderr, "%v\n", e)
return Failure
}
defer engine.listener.Close()

display.Greeting(s)
go func(b *board.Board, s tcell.Screen) {
go func(g *game.Game, s tcell.Screen) {
t := time.NewTicker(10 * time.Millisecond)
for range t.C {
display.Render(b, s)
display.Render(g, s)
}
}(b, s)
}(engine.game, s)

for {
switch ev := s.PollEvent().(type) {
Expand All @@ -130,16 +169,18 @@ func main() {
case *tcell.EventKey:
if ev.Key() == tcell.KeyEscape {
s.Fini()
os.Exit(0)
return Failure
} else {
move := b.ProcessEvent(ev)
var exit int
util.Write(fmt.Sprintf("Event registered: %v", ev))
move := engine.game.RecieveEvent(ev)
util.Write(fmt.Sprintf("Move generated: %v", move))
var rpcExitCode int
if move.Seq > 0 {
g.SendMove(move)
gr.DoMove(move, &exit)
engine.game.SendMove(move)
engine.rpc.DoMove(move, &rpcExitCode)
}

display.Render(b, s)
display.Render(engine.game, s)
}
}
}
Expand Down
88 changes: 88 additions & 0 deletions main_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
package main

import (
"testing"

"github.com/gdamore/tcell/v2"
)

func TestEngine(t *testing.T) {
configA := Config{
HostA: "localhost",
PortA: "1234",
HostB: "localhost",
PortB: "4321",
ID: "A",
Debug: false,
}

configB := configA
configB.ID = "B"

ready := make(chan GameEngine)
go doSetup(t, configA, ready)
go doSetup(t, configB, ready)

var engineA *GameEngine
var engineB *GameEngine

waiting := 2
for {
engine := <-ready
waiting = waiting - 1
if engine.ID == "A" {
engineA = &engine
}
engineB = &engine
if waiting == 0 {
break
}
}

defer engineA.listener.Close()
defer engineB.listener.Close()

actions := []struct {
engine *GameEngine
key tcell.Key
}{
{engineA, tcell.KeyEnter},
{engineA, tcell.KeyUp},
{engineA, tcell.KeyUp},
{engineA, tcell.KeyEnter},
{engineB, tcell.KeyLeft},
{engineB, tcell.KeyEnter},
{engineB, tcell.KeyDown},
{engineB, tcell.KeyDown},
}

for _, action := range actions {
ev := generateKeyPress(action.key)
t.Logf("Executing move %s on engine %s", ev.Name(), action.engine.ID)
t.Logf("Got tcell ev %v", ev)
move := action.engine.game.RecieveEvent(ev)
t.Logf("Got move: %v", move)
t.Logf("Game is %v", action.engine.game)
t.Logf("Board is %v", action.engine.game.GetBoard())
if move.Seq > 0 {
action.engine.game.SendMove(move)
var rpcExitCode int
action.engine.rpc.DoMove(move, &rpcExitCode)
}
}

}

func doSetup(t *testing.T, config Config, ready chan<- GameEngine) {
t.Logf("Setting up with config: %v", config)
running, e := setup(config)
if e != nil {
t.Errorf("Error setting up engine. Config: %v, Error: %v", config, e)
}
t.Logf("Setup succeeded for %s", config.ID)
ready <- running
}

func generateKeyPress(k tcell.Key) *tcell.EventKey {
return tcell.NewEventKey(k, 0, tcell.ModNone)
}

0 comments on commit ef9cb8d

Please sign in to comment.