Fix selecting multiple checkers
parent
9b048ec05d
commit
391fe97215
139
board.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
|
||||
}
|
||||
|
|
|
@ -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
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…
Reference in New Issue