You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
247 lines
6.1 KiB
247 lines
6.1 KiB
package main |
|
|
|
import ( |
|
"strconv" |
|
|
|
"code.rocketnine.space/tslocum/cview" |
|
"code.rocketnine.space/tslocum/fibs" |
|
"github.com/gdamore/tcell/v2" |
|
) |
|
|
|
const SpaceUnknown = -1 |
|
|
|
type GameBoard struct { |
|
Board *fibs.Board |
|
*cview.TextView |
|
|
|
selected [2]int |
|
|
|
dragFromX, dragFromY int |
|
} |
|
|
|
func NewGameBoard(client *fibs.Client) *GameBoard { |
|
b := &GameBoard{ |
|
Board: client.Board, |
|
TextView: cview.NewTextView(), |
|
} |
|
b.TextView.SetRegions(true) |
|
b.TextView.SetDynamicColors(true) |
|
b.TextView.SetToggleHighlights(true) |
|
b.TextView.SetHighlightedFunc(b.handleHighlight) |
|
|
|
b.Update() |
|
return b |
|
} |
|
|
|
func (b *GameBoard) Update() { |
|
b.TextView.SetBytes(b.Board.Render()) |
|
} |
|
|
|
func (b *GameBoard) resetSelection() { |
|
b.selected[0] = 0 |
|
b.selected[1] = 0 |
|
} |
|
|
|
func (b *GameBoard) handleHighlight(added, removed, remaining []string) { |
|
defer b.Update() |
|
|
|
v := b.Board.GetIntState() |
|
|
|
if len(added) > 0 && len(remaining) > 0 { |
|
if added[0] == "space-0" || added[0] == "space-25" { |
|
// Deselect |
|
b.TextView.SetHighlightedFunc(nil) |
|
b.TextView.Highlight(added[0]) |
|
b.TextView.SetHighlightedFunc(b.handleHighlight) |
|
|
|
b.resetSelection() |
|
return |
|
} |
|
|
|
from, err := strconv.Atoi(remaining[0][6:]) |
|
if err != nil { |
|
b.TextView.SetHighlightedFunc(nil) |
|
b.TextView.Highlight(remaining[0]) |
|
b.TextView.SetHighlightedFunc(b.handleHighlight) |
|
|
|
b.resetSelection() |
|
return |
|
} |
|
|
|
to, err := strconv.Atoi(added[0][6:]) |
|
if err != nil { |
|
if added[0] == "space-off" { |
|
to = b.Board.PlayerHomeSpace() |
|
} else { |
|
return |
|
} |
|
} |
|
|
|
var spaces int |
|
calcFrom := from |
|
if to > 0 && to < 25 { |
|
if to >= calcFrom { |
|
spaces = to - calcFrom |
|
} else { |
|
spaces = calcFrom - to |
|
} |
|
} |
|
var mid = SpaceUnknown |
|
if (v[fibs.StatePlayerDice1] != v[fibs.StatePlayerDice2] && spaces*b.selected[1] == v[fibs.StatePlayerDice1]+v[fibs.StatePlayerDice2]) || |
|
(v[fibs.StatePlayerDice1] == v[fibs.StatePlayerDice2] && spaces*b.selected[1] == (v[fibs.StatePlayerDice1]+v[fibs.StatePlayerDice2])*2) { |
|
// Prefer any move that will bar opponent |
|
for i := 0; i < 2; i++ { |
|
dice := v[fibs.StatePlayerDice1] |
|
if i == 2 { |
|
dice = v[fibs.StatePlayerDice2] |
|
} |
|
index := calcFrom + (dice * v[fibs.StateDirection]) |
|
if index == to { |
|
continue |
|
} |
|
if (v[fibs.StateBoardSpace0+index] == -1 && v[fibs.StatePlayerColor] > 0) || |
|
(v[fibs.StateBoardSpace0+index] == 1 && v[fibs.StatePlayerColor] < 0) { |
|
mid = index |
|
break |
|
} |
|
} |
|
if mid == SpaceUnknown { |
|
// Send any valid move |
|
for i := 0; i < 2; i++ { |
|
dice := v[fibs.StatePlayerDice1] |
|
if i == 2 { |
|
dice = v[fibs.StatePlayerDice2] |
|
} |
|
index := calcFrom + (dice * v[fibs.StateDirection]) |
|
if index == to { |
|
continue |
|
} |
|
if (v[fibs.StateBoardSpace0+index] >= 0 && v[fibs.StatePlayerColor] > 0) || |
|
(v[fibs.StateBoardSpace0+index] <= 0 && v[fibs.StatePlayerColor] < 0) { |
|
mid = index |
|
break |
|
} |
|
} |
|
} |
|
} |
|
|
|
for i := 0; i < b.selected[1]; i++ { |
|
if mid < 0 { |
|
b.Board.AddPreMove(from, to) |
|
} else { |
|
b.Board.AddPreMove(from, mid) |
|
b.Board.AddPreMove(mid, to) |
|
} |
|
} |
|
|
|
b.TextView.SetHighlightedFunc(nil) |
|
b.TextView.Highlight(added[0]) |
|
b.TextView.Highlight(remaining[0]) |
|
b.TextView.SetHighlightedFunc(b.handleHighlight) |
|
|
|
b.resetSelection() |
|
} else if len(added) > 0 { |
|
if added[0] == "space-off" { |
|
b.TextView.SetHighlightedFunc(nil) |
|
b.TextView.Highlight(added[0]) |
|
b.TextView.SetHighlightedFunc(b.handleHighlight) |
|
|
|
b.resetSelection() |
|
return |
|
} else if (added[0] == "space-0" || added[0] == "space-25") && v[fibs.StatePlayerBar] == 0 { |
|
b.TextView.SetHighlightedFunc(nil) |
|
b.TextView.Highlight(added[0]) |
|
b.TextView.SetHighlightedFunc(b.handleHighlight) |
|
|
|
b.resetSelection() |
|
return |
|
} |
|
|
|
index, err := strconv.Atoi(added[0][6:]) |
|
if err == nil { |
|
abs := v[fibs.StateBoardSpace0+index] |
|
if abs < 0 { |
|
abs *= -1 |
|
} |
|
if added[0] == "space-0" || added[0] == "space-25" { |
|
abs = v[fibs.StatePlayerBar] |
|
} |
|
|
|
if b.selected[1] >= abs && false { // TODO or has premove piece in space |
|
b.TextView.SetHighlightedFunc(nil) |
|
b.TextView.Highlight(added[0]) |
|
b.TextView.SetHighlightedFunc(b.handleHighlight) |
|
return |
|
} |
|
|
|
b.selected[0] = index |
|
b.selected[1]++ |
|
} |
|
} else if len(removed) > 0 { |
|
index, err := strconv.Atoi(removed[0][6:]) |
|
if err == nil { |
|
abs := v[fibs.StateBoardSpace0+index] |
|
if abs < 0 { |
|
abs *= -1 |
|
} |
|
if removed[0] == "space-0" || removed[0] == "space-25" { |
|
abs = v[fibs.StatePlayerBar] |
|
} |
|
|
|
if b.selected[1] < abs { |
|
b.selected[0] = index |
|
b.selected[1]++ |
|
} |
|
|
|
b.TextView.SetHighlightedFunc(nil) |
|
b.TextView.Highlight(removed[0]) |
|
b.TextView.SetHighlightedFunc(b.handleHighlight) |
|
} |
|
} |
|
} |
|
|
|
// MouseHandler returns the mouse handler for this primitive. |
|
func (b *GameBoard) MouseHandler() func(action cview.MouseAction, event *tcell.EventMouse, setFocus func(p cview.Primitive)) (consumed bool, capture cview.Primitive) { |
|
return b.WrapMouseHandler(func(action cview.MouseAction, event *tcell.EventMouse, setFocus func(p cview.Primitive)) (consumed bool, capture cview.Primitive) { |
|
x, y := event.Position() |
|
if !b.InRect(x, y) { |
|
return false, nil |
|
} |
|
|
|
switch action { |
|
case cview.MouseLeftDown: |
|
b.dragFromX, b.dragFromY = x, y |
|
case cview.MouseLeftUp: |
|
if b.dragFromX != x || b.dragFromY != y { |
|
app.QueueUpdateDraw(func() { |
|
// Simulate click event at start of drag |
|
fromEvent := tcell.NewEventMouse(b.dragFromX, b.dragFromY, tcell.ButtonPrimary, event.Modifiers()) |
|
b.TextView.MouseHandler()(cview.MouseLeftClick, fromEvent, setFocus) |
|
consumed, _ = b.TextView.MouseHandler()(cview.MouseLeftClick, event, setFocus) |
|
if consumed { |
|
// Succeeded |
|
return |
|
} |
|
// Failed, undo |
|
b.TextView.MouseHandler()(cview.MouseLeftClick, fromEvent, setFocus) |
|
}) |
|
} |
|
case cview.MouseRightClick: |
|
b.TextView.SetHighlightedFunc(nil) |
|
h := b.GetHighlights() |
|
for i := range h { |
|
b.Highlight(h[i]) |
|
} |
|
b.TextView.SetHighlightedFunc(b.handleHighlight) |
|
|
|
b.resetSelection() |
|
|
|
b.Update() |
|
|
|
consumed = true |
|
return |
|
} |
|
|
|
return b.TextView.MouseHandler()(action, event, setFocus) |
|
}) |
|
}
|
|
|