Permalink
Browse files

cmd/tictactoe: Display player whose turn it is in bold.

Factor out minimum of 1 second per turn enforcement. Stop displaying
whose turn it is once they've completed their turn (if that happens
before 1 second is up).
  • Loading branch information...
dmitshur committed Oct 10, 2017
1 parent 2233059 commit 37e9c2d9f20c142cf9c90d9ec59586c9b5b420a5
Showing with 60 additions and 34 deletions.
  1. +28 −11 cmd/tictactoe/display.go
  2. +32 −23 cmd/tictactoe/game.go
View
@@ -13,6 +13,7 @@ import (
// page renders the entire page body.
type page struct {
board ttt.Board
turn ttt.State
condition ttt.Condition
errorMessage string
players [2]player
@@ -40,8 +41,8 @@ func (p page) Render() []*html.Node {
htmlg.Div(
// Player X.
style(
`display: inline-block;`,
htmlg.Span(p.players[0].Render()...),
`display: inline-block; width: 200px;`,
htmlg.Span(p.players[0].Render(p.turn)...),
),
// Board.
style(
@@ -50,8 +51,8 @@ func (p page) Render() []*html.Node {
),
// Player O.
style(
`display: inline-block;`,
htmlg.Span(p.players[1].Render()...),
`display: inline-block; width: 200px;`,
htmlg.Span(p.players[1].Render(p.turn)...),
),
),
),
@@ -108,28 +109,44 @@ func (c boardCell) Render() []*html.Node {
return []*html.Node{cell}
}
// Render the player.
func (p player) Render() []*html.Node {
switch imager, ok := p.Player.(ttt.Imager); {
case ok:
// Render the player. turn indicates whose turn it currently is.
func (p player) Render(turn ttt.State) []*html.Node {
switch imager, ok := p.Player.(ttt.Imager); ok {
case true:
var imgStyle string
switch p.Mark {
case ttt.X:
imgStyle = `height: 100px;`
case ttt.O:
imgStyle = `height: 100px; transform: scaleX(-1);`
}
text := htmlg.Text(fmt.Sprintf("%v (%v)", p.Name(), p.Mark))
if p.Mark == turn {
text = &html.Node{
Type: html.ElementNode, Data: atom.Strong.String(),
FirstChild: text,
}
}
return []*html.Node{
style(
imgStyle,
img(imager.Image()),
),
htmlg.Div(htmlg.Text(fmt.Sprintf("%v (%v)", p.Name(), p.Mark))),
htmlg.Div(text),
}
case false:
text := htmlg.Text(fmt.Sprintf("%v (%v)", p.Name(), p.Mark))
if p.Mark == turn {
text = &html.Node{
Type: html.ElementNode, Data: atom.Strong.String(),
FirstChild: text,
}
}
default:
return []*html.Node{
htmlg.Text(fmt.Sprintf("%v (%v)", p.Name(), p.Mark)),
text,
}
default:
panic("unreachable")
}
}
View
@@ -15,8 +15,6 @@ import (
// playGame plays a game of tic-tac-toe with 2 players until the end (Condition != ttt.NotEnd),
// or until an error happens. players[0] always goes first.
func playGame(players [2]player) (ttt.Condition, error) {
var board ttt.Board // Start with an empty board.
// When a board cell is clicked, its [0, 9) index is sent to this channel.
var cellClick chan int
@@ -30,14 +28,20 @@ func playGame(players [2]player) (ttt.Condition, error) {
})
}
fmt.Println()
fmt.Println(board)
if runtime.GOARCH == "js" {
var document = dom.GetWindow().Document().(dom.HTMLDocument)
document.Body().SetInnerHTML(htmlg.Render(page{board: board, players: players}.Render()...))
}
// Start with an empty board.
var board ttt.Board
var condition ttt.Condition
for i := 0; condition == ttt.NotEnd; i++ {
fmt.Println()
fmt.Println(board)
if runtime.GOARCH == "js" {
var document = dom.GetWindow().Document().(dom.HTMLDocument)
document.Body().SetInnerHTML(htmlg.Render(page{board: board, turn: players[i%2].Mark, condition: condition, players: players}.Render()...))
}
turnStart := time.Now()
for i := 0; ; i++ {
err := playerTurn(&board, players[i%2], cellClick)
if err != nil {
if runtime.GOARCH == "js" {
@@ -46,27 +50,35 @@ func playGame(players [2]player) (ttt.Condition, error) {
}
return 0, err
}
condition := board.Condition()
fmt.Println()
fmt.Println(board)
if runtime.GOARCH == "js" {
var document = dom.GetWindow().Document().(dom.HTMLDocument)
document.Body().SetInnerHTML(htmlg.Render(page{board: board, condition: condition, players: players}.Render()...))
}
condition = board.Condition()
// Enforce a minimum of 1 second per turn.
if untilTurnEnd := time.Second - time.Since(turnStart); condition == ttt.NotEnd && untilTurnEnd > 0 {
if runtime.GOARCH == "js" {
var document = dom.GetWindow().Document().(dom.HTMLDocument)
document.Body().SetInnerHTML(htmlg.Render(page{board: board, condition: condition, players: players}.Render()...))
}
if condition != ttt.NotEnd {
return condition, nil
time.Sleep(untilTurnEnd)
}
}
// At this point, the game is over.
fmt.Println()
fmt.Println(board)
if runtime.GOARCH == "js" {
var document = dom.GetWindow().Document().(dom.HTMLDocument)
document.Body().SetInnerHTML(htmlg.Render(page{board: board, condition: condition, players: players}.Render()...))
}
return condition, nil
}
// playerTurn gets the player p's move and applies it to board b.
func playerTurn(b *ttt.Board, player player, cellClick <-chan int) error {
const timePerTurn = 3 * time.Second
started := time.Now()
move, err := playerMove(*b, player, timePerTurn, cellClick)
if err != nil {
return fmt.Errorf("player %v (%s) failed to make a move: %v", player.Mark, player.Name(), err)
@@ -80,9 +92,6 @@ func playerTurn(b *ttt.Board, player player, cellClick <-chan int) error {
return fmt.Errorf("player %v (%s) made a move that isn't legal: %v", player.Mark, player.Name(), err)
}
// Enforce a minimum of 1 second per move.
time.Sleep(time.Second - time.Since(started))
return nil
}

1 comment on commit 37e9c2d

@gopherci

This comment has been minimized.

Show comment
Hide comment
@gopherci

gopherci bot Oct 10, 2017

GopherCI found 1 issue in the last 3 commits, see: https://gci.gopherci.io/analysis/1125

gopherci bot commented on 37e9c2d Oct 10, 2017

GopherCI found 1 issue in the last 3 commits, see: https://gci.gopherci.io/analysis/1125

Please sign in to comment.