Browse Source

Fix selecting multiple checkers

master
Trevor Slocum 3 days ago
parent
commit
391fe97215
  1. 139
      board.go
  2. 6
      board_test.go
  3. 26
      client.go

139
board.go

@ -4,7 +4,6 @@ import (
"bytes"
"fmt"
"log"
"sort"
"strconv"
"strings"
"sync"
@ -73,12 +72,13 @@ type Board struct {
moves [][2]int
movesColor int
validMoves map[int][]int
validMoves map[int][][]int
from map[int]int
to map[int]int
selected [2]int
selectedNum int
selectedSpace int
premove [][2]int
Premovefrom map[int]int
@ -130,8 +130,8 @@ func (b *Board) GetIntState() []int {
}
func (b *Board) resetSelection() {
b.selected[0] = 0
b.selected[1] = 0
b.selectedSpace = 0
b.selectedNum = 0
}
func (b *Board) autoSendMoves() {
@ -142,29 +142,6 @@ func (b *Board) autoSendMoves() {
if b.v[StateMovablePieces] > 0 {
movable = b.v[StateMovablePieces]
}
if len(b.premove) == 1 {
abs := b.premove[0][1] - b.premove[0][0]
direction := 1
if abs < 0 {
abs *= -1
direction = -1
}
if b.v[StatePlayerDice1] == b.v[StatePlayerDice2] {
for expandDoubles := 4; expandDoubles >= 2; expandDoubles-- {
if abs != b.v[StatePlayerDice1]*expandDoubles {
continue
}
from, _ := b.premove[0][0], b.premove[0][1]
b.premove = nil
for i := 1; i <= expandDoubles; i++ {
b.premove = append(b.premove, [2]int{from + ((b.v[StatePlayerDice1]*i - 1) * direction), from + ((b.v[StatePlayerDice1] * i) * direction)})
}
break
}
}
}
if len(b.premove) < movable {
return
}
@ -370,10 +347,10 @@ func (b *Board) renderSpace(index int, spaceValue int) []byte {
}
}
// Highlight legal moves
highlightSpace := b.ValidMove(b.selected[0], index)
highlightSpace := b.ValidMove(b.selectedSpace, index)
highlightSpace = false // TODO Make configurable, disable by default
//+(b.v[StatePlayerDice1]*b.v[StatePlayerColor]) ||b.selected[0] == index+(b.v[StatePlayerDice2]*b.v[StatePlayerColor])) && b.selected[1] > 0
if b.selected[1] > 0 && highlightSpace && index != 25 && index != 0 {
//+(b.v[StatePlayerDice1]*b.v[StatePlayerColor]) ||b.selectedSpace == index+(b.v[StatePlayerDice2]*b.v[StatePlayerColor])) && b.selectedNum > 0
if b.selectedNum > 0 && highlightSpace && index != 25 && index != 0 {
foregroundColor = "black"
backgroundColor = "yellow"
}
@ -384,7 +361,7 @@ func (b *Board) renderSpace(index int, spaceValue int) []byte {
}
rightArrowFrom := (b.v[StateDirection] == b.movesColor) == (index > 12)
if b.selected[0] == index && b.selected[1] > 0 && spaceValue <= abs && spaceValue > abs-b.selected[1] {
if b.selectedSpace == index && b.selectedNum > 0 && spaceValue <= abs && spaceValue > abs-b.selectedNum {
r = []byte("*")
} else if b.Premovefrom[index] > 0 && spaceValue > (abs+b.Premoveto[index])-b.Premovefrom[index] && spaceValue <= abs+b.Premoveto[index] {
if index == 25-b.PlayerBarSpace() {
@ -425,7 +402,7 @@ func (b *Board) renderSpace(index int, spaceValue int) []byte {
func (b *Board) ResetMoves() {
b.moves = nil
b.movesColor = 0
b.validMoves = make(map[int][]int)
b.validMoves = make(map[int][][]int)
b.from = make(map[int]int)
b.to = make(map[int]int)
}
@ -453,9 +430,15 @@ func (b *Board) allPlayerPiecesInHomeBoard() bool {
return false
}
value := b.v[StateBoardSpace0+index]
// Include pre-moves
mod := b.v[StatePlayerColor]
value -= b.client.Board.Premovefrom[index] * mod
return value != 0
if b.v[StatePlayerColor] == -1 {
return value < 0
}
return value > 0
}
for i := 1; i < 24; i++ {
if i >= homeBoardStart && i <= homeBoardEnd {
@ -479,12 +462,12 @@ func (b *Board) spaceAvailable(index int) bool {
(b.v[StatePlayerColor] == -1 && b.v[StateBoardSpace0+index] <= 1)
}
func (b *Board) GetValidMoves(from int) []int {
func (b *Board) GetValidMoves(from int) [][]int {
if validMoves, ok := b.validMoves[from]; ok {
return validMoves
}
var validMoves []int
var validMoves [][]int
defer func() {
b.validMoves[from] = validMoves
}()
@ -493,7 +476,6 @@ func (b *Board) GetValidMoves(from int) []int {
return validMoves
}
// TODO consider opponent blocking midway In full move
trySpaces := [][]int{
{b.v[StatePlayerDice1]},
{b.v[StatePlayerDice2]},
@ -529,12 +511,10 @@ CHECKSPACES:
space := from + (checkSpace * b.v[StateDirection])
if _, value := foundMoves[space]; !value {
foundMoves[space] = true
validMoves = append(validMoves, space)
validMoves = append(validMoves, trySpaces[i])
}
}
sort.Ints(validMoves)
return validMoves
}
@ -560,8 +540,16 @@ func (b *Board) ValidMove(f int, t int) bool {
}
validMoves := b.GetValidMoves(f)
CHECKVALID:
for i := range validMoves {
if validMoves[i] == t {
checkSpace := 0
for _, space := range validMoves[i] {
checkSpace += space
if !b.spaceAvailable(f + (checkSpace * b.v[StateDirection])) {
continue CHECKVALID
}
}
if f+(checkSpace*b.v[StateDirection]) == t {
return true
}
}
@ -603,7 +591,7 @@ func (b *Board) Move(player int, f string, t string) {
b.v[StateTurn] = player * -1
b.validMoves = make(map[int][]int)
b.validMoves = make(map[int][][]int)
b.ResetPreMoves()
}
@ -627,18 +615,77 @@ func (b *Board) SimplifyMoves() {
}
}
func (b *Board) AddPreMove(from int, to int) bool {
func (b *Board) GetSelection() (num int, space int) {
return b.selectedNum, b.selectedSpace
}
func (b *Board) SetSelection(num int, space int) {
b.selectedNum, b.selectedSpace = num, space
}
func (b *Board) ResetSelection() {
b.selectedNum, b.selectedSpace = 0, 0
}
func (b *Board) addPreMove(from int, to int, num int) bool {
// Allow bearing off when the player moves their own pieces on to the bar
if to == 0 || to == 25 {
to = b.PlayerHomeSpace()
}
// Expand combined move
moves := b.client.Board.GetValidMoves(from)
CHECKPREMOVES:
for i := range moves {
checkSpace := 0
for _, space := range moves[i] {
checkSpace += space
lf("CHECK %d %d", checkSpace, from+(checkSpace*b.v[StateDirection]))
if !b.spaceAvailable(from + (checkSpace * b.v[StateDirection])) {
continue CHECKPREMOVES
}
}
lf("SECOND PHASE %+v %d", moves[i], num)
if (from+(checkSpace*b.v[StateDirection]) == to) && len(moves[i]) > 1 {
lf("SECOND.5 PHASE %+v %d", moves[i], num)
for j := 0; j < num; j++ {
checkSpace = 0
lastSpace := 0
for _, space := range moves[i] {
checkSpace += space
lf("THIRD PHASE %d %d", from+(lastSpace*b.v[StateDirection]), from+(checkSpace*b.v[StateDirection]))
if !b.addPreMove(from+(lastSpace*b.v[StateDirection]), from+(checkSpace*b.v[StateDirection]), 1) {
return false
}
lastSpace = checkSpace
}
}
return true
}
}
if !b.ValidMove(from, to) {
return false
}
b.premove = append(b.premove, [2]int{from, to})
b.Premovefrom[from]++
b.Premoveto[to]++
for i := 0; i < num; i++ {
b.premove = append(b.premove, [2]int{from, to})
b.Premovefrom[from]++
b.Premoveto[to]++
}
lf("ADD %+v", b.premove)
return true
}
func (b *Board) AddPreMove(from int, to int) bool {
if !b.addPreMove(from, to, b.selectedNum) {
return false
}
lf("FINAL %+v", b.premove)
b.resetSelection()
b.autoSendMoves()
return true
}

6
board_test.go

@ -42,8 +42,10 @@ func TestBoard_GetValidMoves(t *testing.T) {
b.Draw()
validMoves := b.GetValidMoves(c.from)
if !equalInts(validMoves, c.moves) {
t.Errorf("unexpected valid moves: expected %+v, got %+v\n%s", c.moves, validMoves, b.Render())
for i := range validMoves {
if !equalInts(validMoves[i], c.moves) {
t.Errorf("unexpected valid moves: expected %+v, got %+v\n%s", c.moves, validMoves, b.Render())
}
}
})
}

26
client.go

@ -19,7 +19,8 @@ import (
"nhooyr.io/websocket"
)
const debug = 1 // TODO
// Debug controls the level of debug information to print.
var Debug = 0
const whoInfoSize = 12
@ -209,7 +210,7 @@ func (c *Client) handleWrite() {
c.rawMode = true
}
if debug > 0 {
if Debug > 0 {
l("-> " + string(bytes.TrimSpace(b)))
}
@ -266,7 +267,7 @@ func (c *Client) handleRead(r io.Reader) {
buf := make([]byte, b.Len())
copy(buf, b.Bytes())
if debug > 0 {
if Debug > 0 {
l("<- " + string(bytes.TrimSpace(buf)))
}
@ -475,16 +476,8 @@ func (c *Client) callWebSocket() {
}()
c.handleWrite()
/* TODO
err := conn.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, ""))
if err != nil {
log.Println("write close:", err)
return nil
}
select {
case <-done:
case <-time.After(time.Second):
}*/
// TODO write CloseMessage when closing WS
}
func (c *Client) Connect() error {
@ -519,6 +512,7 @@ func (c *Client) eventLoop() {
var winsMatchRegexp = regexp.MustCompile(`^\w+ wins a [0-9]+ point match against .*`)
var winsThisMatchRegexp = regexp.MustCompile(`^\w+ wins the [0-9]+ point match .*`)
var newGameRegexp = regexp.MustCompile(`^Starting a new game with .*`)
var inviteResumeGameRegexp = regexp.MustCompile(`^*\* You invited \w+ to resume a saved match\..*`)
var joinedGameRegexp = regexp.MustCompile(`^\w+ has joined you\..*`)
var gameBufferRegexp = regexp.MustCompile(`^\w+ (makes|roll|rolls|rolled|move|moves) .*`)
@ -734,6 +728,12 @@ func (c *Client) eventLoop() {
} else if bytes.HasPrefix(bl, []byte("you're now watching")) {
// Board state is not sent automatically when watching
c.Out <- []byte("board")
} else if inviteResumeGameRegexp.Match(b) {
// Board state is not always sent automatically when joining resumed game
go func() {
time.Sleep(500 * time.Millisecond)
c.Out <- []byte("board")
}()
} else if logInOutRegexp.Match(b) {
continue
} else if dropsConnection.Match(b) {

Loading…
Cancel
Save