Draw checkers in home spaces sideways

Resolves #10.
This commit is contained in:
Trevor Slocum 2024-01-19 23:33:45 -08:00
parent 539f35bf53
commit 626d1d088b
8 changed files with 128 additions and 93 deletions

View file

@ -1,3 +1,6 @@
1.2.7:
- Draw checkers in home spaces sideways
1.2.6:
- Add rating column to list of matches
- Add rating information to history screen

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.4 KiB

View file

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

View file

@ -1269,7 +1269,7 @@ func (b *board) toggleAdvancedMovementCheckbox() error {
func (b *board) newSprite(white bool) *Sprite {
s := &Sprite{}
s.colorWhite = white
s.w, s.h = imgCheckerLight.Bounds().Dx(), imgCheckerLight.Bounds().Dy()
s.w, s.h = imgCheckerTop.Bounds().Dx(), imgCheckerTop.Bounds().Dy()
return s
}
@ -1464,6 +1464,46 @@ func (b *board) updateBackgroundImage() {
}
}
func (b *board) drawChecker(target *ebiten.Image, checker *ebiten.Image, x float64, y float64, white bool, side bool) {
// Draw shadow.
if !side {
op := &ebiten.DrawImageOptions{}
op.Filter = ebiten.FilterLinear
op.GeoM.Translate(x, y)
op.ColorScale.Scale(0, 0, 0, 1)
target.DrawImage(checker, op)
}
// Draw checker.
checkerScale := 0.94
height := b.spaceWidth
if side {
height = 80
}
op := &ebiten.DrawImageOptions{}
op.Filter = ebiten.FilterLinear
op.GeoM.Translate(-b.spaceWidth/2, -height/2)
op.GeoM.Scale(checkerScale, checkerScale)
op.GeoM.Translate((b.spaceWidth/2)+x, (height/2)+y)
c := lightCheckerColor
if !white {
c = darkCheckerColor
}
op.ColorScale.Scale(0, 0, 0, 1)
r := float32(c.R) / 0xff
g := float32(c.G) / 0xff
bl := float32(c.B) / 0xff
op.ColorScale.SetR(r)
op.ColorScale.SetG(g)
op.ColorScale.SetB(bl)
target.DrawImage(checker, op)
}
func (b *board) drawSprite(target *ebiten.Image, sprite *Sprite) {
x, y := float64(sprite.x), float64(sprite.y)
if sprite == b.dragging {
@ -1507,39 +1547,7 @@ func (b *board) drawSprite(target *ebiten.Image, sprite *Sprite) {
// Schedule another frame
scheduleFrame()
}
// Draw shadow.
{
op := &ebiten.DrawImageOptions{}
op.Filter = ebiten.FilterLinear
op.GeoM.Translate(x, y)
op.ColorScale.Scale(0, 0, 0, 1)
target.DrawImage(imgCheckerLight, op)
}
// Draw checker.
checkerScale := 0.94
op := &ebiten.DrawImageOptions{}
op.Filter = ebiten.FilterLinear
op.GeoM.Translate(-b.spaceWidth/2, -b.spaceWidth/2)
op.GeoM.Scale(checkerScale, checkerScale)
op.GeoM.Translate((b.spaceWidth/2)+x, (b.spaceWidth/2)+y)
c := lightCheckerColor
if !sprite.colorWhite {
c = darkCheckerColor
}
op.ColorScale.Scale(0, 0, 0, 1)
r := float32(c.R) / 0xff
g := float32(c.G) / 0xff
bl := float32(c.B) / 0xff
op.ColorScale.SetR(r)
op.ColorScale.SetG(g)
op.ColorScale.SetB(bl)
target.DrawImage(imgCheckerLight, op)
b.drawChecker(target, imgCheckerTop, x, y, sprite.colorWhite, false)
}
func (b *board) innerBoardCenter(right bool) int {
@ -1557,6 +1565,9 @@ func (b *board) Draw(screen *ebiten.Image) {
}
for space := int8(0); space < bgammon.BoardSpaces; space++ {
if space == bgammon.SpaceHomePlayer || space == bgammon.SpaceHomeOpponent {
continue
}
var numPieces int8
for i, sprite := range b.spaceSprites[space] {
if sprite == b.dragging || sprite == b.moving {
@ -1602,6 +1613,21 @@ func (b *board) Draw(screen *ebiten.Image) {
}
}
r := b.spaceRects[bgammon.SpaceHomePlayer]
checkerY := float64(b.y+int(b.verticalBorderSize)+r[1]+r[3]) + 3
checkerHeight := (b.spaceWidth + b.overlapSize*4) / 15
checkers := len(b.spaceSprites[bgammon.SpaceHomePlayer])
for i := 0; i < checkers; i++ {
b.drawChecker(screen, imgCheckerSide, float64(b.x+int(b.horizontalBorderSize)+r[0]), float64(checkerY-checkerHeight*float64(i+1)), b.flipBoard, true)
}
r = b.spaceRects[bgammon.SpaceHomeOpponent]
checkerY = float64(b.y+int(b.verticalBorderSize)+int(b.spaceWidth)+int(b.overlapSize*4)) + 1
checkers = len(b.spaceSprites[bgammon.SpaceHomeOpponent])
for i := 0; i < checkers; i++ {
b.drawChecker(screen, imgCheckerSide, float64(b.x+int(b.horizontalBorderSize)+r[0]), float64(checkerY-checkerHeight*float64(i+1)), !b.flipBoard, true)
}
b.stateLock.Lock()
var highlightSpaces [][]int8
dragging := b.dragging
@ -1836,7 +1862,7 @@ func (b *board) setRect(x, y, w, h int) {
for i := 0; i < b.Sprites.num; i++ {
s := b.Sprites.sprites[i]
s.w, s.h = imgCheckerLight.Bounds().Dx(), imgCheckerLight.Bounds().Dy()
s.w, s.h = imgCheckerTop.Bounds().Dx(), imgCheckerTop.Bounds().Dy()
}
b.setSpaceRects()
@ -2250,6 +2276,10 @@ func (b *board) processState() {
b.stateLock.Lock()
defer b.stateLock.Unlock()
if b.dragging != nil {
return
}
if b.lastPlayerNumber != b.gameState.PlayerNumber || b.lastVariant != b.gameState.Variant {
b.setSpaceRects()
b.updateBackgroundImage()
@ -2540,6 +2570,13 @@ func (b *board) _movePiece(sprite *Sprite, from int8, to int8, speed int8, pause
space := to // Immediately go to target space
for i, s := range b.spaceSprites[from] {
if s == sprite {
b.spaceSprites[from] = append(b.spaceSprites[from][:i], b.spaceSprites[from][i+1:]...)
break
}
}
stack := len(b.spaceSprites[space])
if stack == 1 && sprite.colorWhite != b.spaceSprites[space][0].colorWhite {
stack = 0 // Hit
@ -2566,12 +2603,6 @@ func (b *board) _movePiece(sprite *Sprite, from int8, to int8, speed int8, pause
sprite.toStart = time.Time{}
b.spaceSprites[to] = append(b.spaceSprites[to], sprite)
for i, s := range b.spaceSprites[from] {
if s == sprite {
b.spaceSprites[from] = append(b.spaceSprites[from][:i], b.spaceSprites[from][i+1:]...)
break
}
}
b.moving = nil
if pauseTime == 0 {
@ -2621,6 +2652,16 @@ func (b *board) playerTurn() bool {
func (b *board) startDrag(s *Sprite, space int8, click bool) {
b.dragging = s
if space >= 0 && space < bgammon.BoardSpaces {
for i, sprite := range b.spaceSprites[space] {
if s == sprite {
b.spaceSprites[space] = append(b.spaceSprites[space][:i], b.spaceSprites[space][i+1:]...)
break
}
}
}
b.draggingSpace = space
b.draggingClick = click
b.lastDragClick = time.Now()
@ -2679,45 +2720,37 @@ func (b *board) finishDrag(x int, y int, click bool) {
var processed bool
if index >= 0 && b.Client != nil {
ADDPREMOVE:
for sp, pieces := range b.spaceSprites {
space := int8(sp)
for _, piece := range pieces {
if piece == dropped {
if space != index {
playSoundEffect(effectMove)
b.gameState.AddLocalMove([]int8{space, index})
b.processState()
scheduleFrame()
processed = true
b.Client.Out <- []byte(fmt.Sprintf("mv %d/%d", space, index))
} else if time.Since(b.lastDragClick) < 500*time.Millisecond && b.gameState.MayBearOff(b.gameState.PlayerNumber, true) {
homeStart, homeEnd := bgammon.HomeRange(b.gameState.PlayerNumber, b.gameState.Variant)
if homeEnd < homeStart {
homeStart, homeEnd = homeEnd, homeStart
}
if index >= homeStart && index <= homeEnd {
b.Client.Out <- []byte(fmt.Sprintf("mv %d/off", index))
}
} else if time.Since(b.lastDragClick) < 500*time.Millisecond && space == bgammon.SpaceHomePlayer && !b.gameState.Player1.Entered {
var found bool
for _, m := range b.gameState.Available {
if m[0] == bgammon.SpaceHomePlayer && bgammon.SpaceDiff(m[0], m[1], b.gameState.Variant) == b.gameState.Roll1 {
b.Client.Out <- []byte(fmt.Sprintf("mv %d/%d", m[0], m[1]))
found = true
break
}
}
if !found {
for _, m := range b.gameState.Available {
if m[0] == bgammon.SpaceHomePlayer {
b.Client.Out <- []byte(fmt.Sprintf("mv %d/%d", m[0], m[1]))
break
}
}
}
space := b.draggingSpace
if space != index {
playSoundEffect(effectMove)
b.gameState.AddLocalMove([]int8{space, index})
b.processState()
scheduleFrame()
processed = true
b.Client.Out <- []byte(fmt.Sprintf("mv %d/%d", space, index))
} else if time.Since(b.lastDragClick) < 500*time.Millisecond && b.gameState.MayBearOff(b.gameState.PlayerNumber, true) {
homeStart, homeEnd := bgammon.HomeRange(b.gameState.PlayerNumber, b.gameState.Variant)
if homeEnd < homeStart {
homeStart, homeEnd = homeEnd, homeStart
}
if index >= homeStart && index <= homeEnd {
b.Client.Out <- []byte(fmt.Sprintf("mv %d/off", index))
}
} else if time.Since(b.lastDragClick) < 500*time.Millisecond && space == bgammon.SpaceHomePlayer && !b.gameState.Player1.Entered {
var found bool
for _, m := range b.gameState.Available {
if m[0] == bgammon.SpaceHomePlayer && bgammon.SpaceDiff(m[0], m[1], b.gameState.Variant) == b.gameState.Roll1 {
b.Client.Out <- []byte(fmt.Sprintf("mv %d/%d", m[0], m[1]))
found = true
break
}
}
if !found {
for _, m := range b.gameState.Available {
if m[0] == bgammon.SpaceHomePlayer {
b.Client.Out <- []byte(fmt.Sprintf("mv %d/%d", m[0], m[1]))
break
}
break ADDPREMOVE
}
}
}

View file

@ -57,8 +57,8 @@ var assetFS embed.FS
var debugExtra []byte
var (
imgCheckerLight *ebiten.Image
//imgCheckerDark *ebiten.Image
imgCheckerTop *ebiten.Image
imgCheckerSide *ebiten.Image
imgDice *ebiten.Image
imgDice1 *ebiten.Image
@ -223,9 +223,8 @@ func loadImageAssets(width int) {
}
loadedCheckerWidth = width
imgCheckerLight = loadAsset("asset/image/checker_white.png", width)
//imgCheckerDark = loadAsset("asset/image/checker_white.png", width)
//imgCheckerDark = loadAsset("assets/checker_black.png", width)
imgCheckerTop = loadAsset("asset/image/checker_top.png", width)
imgCheckerSide = loadAsset("asset/image/checker_side.png", width)
resizeDice := func(img image.Image) *ebiten.Image {
if game == nil {

6
go.mod
View file

@ -4,8 +4,8 @@ go 1.17
require (
code.rocket9labs.com/tslocum/bgammon v0.0.0-20240117214045-3607efee4129
code.rocket9labs.com/tslocum/bgammon-tabula-bot v0.0.0-20240118194149-d51931e431f7
code.rocket9labs.com/tslocum/etk v0.0.0-20240118083053-9eecc7a44b04
code.rocket9labs.com/tslocum/bgammon-tabula-bot v0.0.0-20240120044556-60982417c592
code.rocket9labs.com/tslocum/etk v0.0.0-20240119041154-d2c44cc232f6
code.rocket9labs.com/tslocum/tabula v0.0.0-20240118055336-21a3dea3f702
github.com/hajimehoshi/ebiten/v2 v2.6.3
github.com/leonelquinteros/gotext v1.5.3-0.20231003122255-12a99145a351
@ -54,7 +54,7 @@ require (
github.com/vanng822/css v1.0.1 // indirect
github.com/vanng822/go-premailer v1.20.2 // indirect
golang.org/x/crypto v0.18.0 // indirect
golang.org/x/exp/shiny v0.0.0-20240112132812-db7319d0e0e3 // indirect
golang.org/x/exp/shiny v0.0.0-20240119083558-1b970713d09a // indirect
golang.org/x/mobile v0.0.0-20240112133503-c713f31d574b // indirect
golang.org/x/net v0.20.0 // indirect
golang.org/x/sync v0.6.0 // indirect

12
go.sum
View file

@ -2,10 +2,10 @@ code.rocket9labs.com/tslocum/bei v0.0.0-20240108012722-6db380cc190b h1:Y0a14Kf/h
code.rocket9labs.com/tslocum/bei v0.0.0-20240108012722-6db380cc190b/go.mod h1:tS60/VNAJphKvDBkSLQhKALa15msIAuWWfEKNc4oFZc=
code.rocket9labs.com/tslocum/bgammon v0.0.0-20240117214045-3607efee4129 h1:wguf0figCFqnSxAK8wAznDor28ZEkElA2ShoZmqLRHg=
code.rocket9labs.com/tslocum/bgammon v0.0.0-20240117214045-3607efee4129/go.mod h1:LAki3jpHOsr4fwaK0xC9tkg+wgu/9ZNEqqx1zE3/HP4=
code.rocket9labs.com/tslocum/bgammon-tabula-bot v0.0.0-20240118194149-d51931e431f7 h1:suit9TmttidcpYqMSLwcUeB2Ks0y5vw19xtH38Vw+4s=
code.rocket9labs.com/tslocum/bgammon-tabula-bot v0.0.0-20240118194149-d51931e431f7/go.mod h1:w6xR2lWW3xUo7KlCOaJ+Jbs29AfQVRQEJ2W1vb0IuOo=
code.rocket9labs.com/tslocum/etk v0.0.0-20240118083053-9eecc7a44b04 h1:RgEI91LXhZeeb4Hje+dXLeL/CMy0WLfNzyBEFXkiOOw=
code.rocket9labs.com/tslocum/etk v0.0.0-20240118083053-9eecc7a44b04/go.mod h1:+mJqiyL/Ne30kcpnaQ1+XjYxI3PZA7HfWY0ReHeSMEA=
code.rocket9labs.com/tslocum/bgammon-tabula-bot v0.0.0-20240120044556-60982417c592 h1:vLCTYMZfu6otPNNQG/vgFibct+p1fsKGxk1eCddRAi4=
code.rocket9labs.com/tslocum/bgammon-tabula-bot v0.0.0-20240120044556-60982417c592/go.mod h1:w6xR2lWW3xUo7KlCOaJ+Jbs29AfQVRQEJ2W1vb0IuOo=
code.rocket9labs.com/tslocum/etk v0.0.0-20240119041154-d2c44cc232f6 h1:uXG3IWWGkMG/mDjk1YsWJfQMbssYh3weoGuz1knFSuE=
code.rocket9labs.com/tslocum/etk v0.0.0-20240119041154-d2c44cc232f6/go.mod h1:+mJqiyL/Ne30kcpnaQ1+XjYxI3PZA7HfWY0ReHeSMEA=
code.rocket9labs.com/tslocum/tabula v0.0.0-20240118055336-21a3dea3f702 h1:NGZILSBynzLZF84WKZyAuoWsZFq37uvEcP2dFvPsY/8=
code.rocket9labs.com/tslocum/tabula v0.0.0-20240118055336-21a3dea3f702/go.mod h1:WEJXESKXqrMFLAArikQ79lpRibNeeE1C0VruxXYMF5M=
github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI=
@ -136,8 +136,8 @@ golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5y
golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
golang.org/x/crypto v0.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc=
golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg=
golang.org/x/exp/shiny v0.0.0-20240112132812-db7319d0e0e3 h1:NezsOJwoBjJ5AXH5QQCdxe+WsqLw+f/t8eo1Tacfhqs=
golang.org/x/exp/shiny v0.0.0-20240112132812-db7319d0e0e3/go.mod h1:3F+MieQB7dRYLTmnncoFbb1crS5lfQoTfDgQy6K4N0o=
golang.org/x/exp/shiny v0.0.0-20240119083558-1b970713d09a h1:NZ9mAQhIcCceDZKqQX3JJVIz7nn3QLDuC+nXedsViBM=
golang.org/x/exp/shiny v0.0.0-20240119083558-1b970713d09a/go.mod h1:3F+MieQB7dRYLTmnncoFbb1crS5lfQoTfDgQy6K4N0o=
golang.org/x/image v0.15.0 h1:kOELfmgrmJlw4Cdb7g/QGuB3CvDrXbqEIww/pNtNBm8=
golang.org/x/image v0.15.0/go.mod h1:HUYqC05R2ZcZ3ejNQsIHQDQiwWM4JBqmm6MKANTp4LE=
golang.org/x/mobile v0.0.0-20240112133503-c713f31d574b h1:kfWLZgb8iUBHdE9WydD5V5dHIS/F6HjlBZNyJfn2bs4=