chessbot/chess/board.go

814 lines
16 KiB
Go
Raw Normal View History

2024-09-18 01:15:24 +00:00
package chess
import (
"fmt"
"image"
"image/draw"
"image/png"
"log"
"os"
2024-09-18 02:33:58 +00:00
"strings"
2024-09-25 19:29:32 +00:00
"golang.org/x/image/font"
"golang.org/x/image/font/basicfont"
"golang.org/x/image/math/fixed"
2024-09-18 01:15:24 +00:00
)
type Board struct {
tiles [8][8]*Piece
2024-09-18 02:33:58 +00:00
turn Color
2024-09-23 04:07:34 +00:00
moves []string
2024-09-18 01:15:24 +00:00
}
func NewBoard() *Board {
2024-09-18 02:33:58 +00:00
board := &Board{turn: Light}
2024-09-18 01:15:24 +00:00
board.mustSetPiece(Rook, Light, "a1")
board.mustSetPiece(Knight, Light, "b1")
board.mustSetPiece(Bishop, Light, "c1")
board.mustSetPiece(Queen, Light, "d1")
board.mustSetPiece(King, Light, "e1")
board.mustSetPiece(Bishop, Light, "f1")
board.mustSetPiece(Knight, Light, "g1")
board.mustSetPiece(Rook, Light, "h1")
board.mustSetPiece(Pawn, Light, "a2")
board.mustSetPiece(Pawn, Light, "b2")
board.mustSetPiece(Pawn, Light, "c2")
board.mustSetPiece(Pawn, Light, "d2")
board.mustSetPiece(Pawn, Light, "e2")
board.mustSetPiece(Pawn, Light, "f2")
board.mustSetPiece(Pawn, Light, "g2")
board.mustSetPiece(Pawn, Light, "h2")
board.mustSetPiece(Rook, Dark, "a8")
board.mustSetPiece(Knight, Dark, "b8")
board.mustSetPiece(Bishop, Dark, "c8")
board.mustSetPiece(Queen, Dark, "d8")
board.mustSetPiece(King, Dark, "e8")
board.mustSetPiece(Bishop, Dark, "f8")
board.mustSetPiece(Knight, Dark, "g8")
board.mustSetPiece(Rook, Dark, "h8")
board.mustSetPiece(Pawn, Dark, "a7")
board.mustSetPiece(Pawn, Dark, "b7")
board.mustSetPiece(Pawn, Dark, "c7")
board.mustSetPiece(Pawn, Dark, "d7")
board.mustSetPiece(Pawn, Dark, "e7")
board.mustSetPiece(Pawn, Dark, "f7")
board.mustSetPiece(Pawn, Dark, "g7")
board.mustSetPiece(Pawn, Dark, "h7")
return board
}
2024-09-23 04:52:58 +00:00
func NewGame(moves string) (*Board, error) {
2024-09-23 00:55:55 +00:00
board := NewBoard()
2024-09-23 04:52:58 +00:00
if err := board.Parse(moves); err != nil {
2024-09-23 00:55:55 +00:00
return nil, err
}
return board, nil
}
2024-09-18 01:15:24 +00:00
func (b *Board) Save(filename string) error {
var (
2024-09-23 00:55:55 +00:00
file *os.File
err error
2024-09-18 01:15:24 +00:00
)
if file, err = os.Create(filename); err != nil {
return err
}
defer file.Close()
2024-09-23 00:55:55 +00:00
return png.Encode(file, b.Image())
}
func (b *Board) Image() *image.RGBA {
var (
img *image.RGBA
piece *Piece
bg *image.Uniform
rect image.Rectangle
p = image.Point{0, 0}
)
2024-09-18 01:15:24 +00:00
img = image.NewRGBA(image.Rect(0, 0, 1024, 1024))
for yi := 0; yi < 8; yi++ {
for xi := 0; xi < 8; xi++ {
2024-09-25 19:29:32 +00:00
x := xi * 128
y := yi * 128
rect = image.Rect(x, y, x+128, y+128)
2024-09-18 01:15:24 +00:00
bg = image.NewUniform(getTileColor(xi, yi))
draw.Draw(img, rect, bg, p, draw.Src)
piece = b.tiles[xi][yi]
if piece != nil {
2024-09-23 04:25:23 +00:00
pieceImg := piece.Image
if b.turn == Dark {
pieceImg = flipImage(pieceImg)
}
draw.Draw(img, rect, pieceImg, p, draw.Over)
2024-09-18 01:15:24 +00:00
}
}
}
2024-09-23 04:25:23 +00:00
if b.turn == Dark {
2024-09-25 19:29:32 +00:00
img = flipImage(img)
}
for yi := 0; yi < 8; yi++ {
for xi := 0; xi < 8; xi++ {
if b.turn == Light {
drawCoordinate(img, xi, yi, false)
}
if b.turn == Dark {
drawCoordinate(img, xi, yi, true)
}
}
2024-09-23 04:25:23 +00:00
}
2024-09-23 00:55:55 +00:00
return img
2024-09-18 01:15:24 +00:00
}
2024-09-25 19:29:32 +00:00
func drawCoordinate(img *image.RGBA, x, y int, flipped bool) {
if x != 7 && y != 7 {
return
}
var column, row string
if y == 7 {
switch x {
case 0:
column = "a"
case 1:
column = "b"
case 2:
column = "c"
case 3:
column = "d"
case 4:
column = "e"
case 5:
column = "f"
case 6:
column = "g"
case 7:
column = "h"
}
}
if x == 7 {
yRow := y
if flipped {
yRow = 7 - y
}
switch yRow {
case 0:
row = "8"
case 1:
row = "7"
case 2:
row = "6"
case 3:
row = "5"
case 4:
row = "4"
case 5:
row = "3"
case 6:
row = "2"
case 7:
row = "1"
}
}
drawString := func(s string, origin fixed.Point26_6) {
color := getTileColor(x, y)
if !flipped && color == Light {
color = Dark
} else if !flipped {
color = Light
}
// TODO: use SN font and make it bold
d := &font.Drawer{
Dst: img,
Src: image.NewUniform(color),
Face: basicfont.Face7x13,
Dot: origin,
}
d.DrawString(s)
}
var origin fixed.Point26_6
if column != "" {
origin = fixed.P(x*128+5, (y+1)*128-5)
drawString(column, origin)
}
if row != "" {
origin = fixed.P((x+1)*128-12, y*128+15)
drawString(row, origin)
}
}
2024-09-18 01:15:24 +00:00
func (b *Board) SetPiece(name PieceName, color Color, position string) error {
var (
piece *Piece
x int
y int
err error
)
if len(position) != 2 {
return fmt.Errorf("invalid position: %s", position)
}
if piece, err = NewPiece(name, color); err != nil {
return err
}
if x, y, err = getXY(position); err != nil {
return err
}
b.tiles[x][y] = piece
return nil
}
2024-09-23 04:07:34 +00:00
func (b *Board) AlgebraicNotation() string {
var text string
for i, m := range b.moves {
if i%2 == 0 {
text += fmt.Sprintf("%d. %s", i/2+1, m)
} else {
text += fmt.Sprintf(" %s ", m)
}
}
2024-09-23 04:52:58 +00:00
return fmt.Sprintf("`%s`", text)
2024-09-23 04:07:34 +00:00
}
2024-09-18 02:33:58 +00:00
func (b *Board) Parse(pgn string) error {
var (
2024-09-23 04:52:58 +00:00
moves = strings.Split(strings.Trim(pgn, " "), " ")
2024-09-18 02:33:58 +00:00
err error
)
for _, move := range moves {
2024-09-23 04:52:58 +00:00
move = strings.Trim(move, " ")
if move == "" {
continue
}
2024-09-18 02:33:58 +00:00
if err = b.Move(move); err != nil {
return err
}
}
return nil
}
func (b *Board) Move(move string) error {
2024-09-18 02:33:58 +00:00
var (
piece string
targetPosition string
2024-09-18 05:59:15 +00:00
// the column from which the piece is captured.
// for example, this would be 'e' for exd4 and 'e' for Nexd4.
captureFrom string
collisionPiece *Piece
err error
2024-09-18 02:33:58 +00:00
)
if strings.Contains(move, "x") {
2024-09-18 05:59:15 +00:00
// capture move
parts := strings.Split(move, "x")
2024-09-18 05:59:15 +00:00
if len(parts) != 2 {
return fmt.Errorf("invalid move: %s", move)
2024-09-18 05:59:15 +00:00
}
if len(parts[0]) == 2 {
// example: Nexd4
captureFrom = parts[0][1:]
} else if len(parts[0]) == 1 {
if strings.ToLower(parts[0]) == parts[0] {
// example: exd4
captureFrom = parts[0]
} else {
// example: Nxd4
captureFrom = ""
}
} else {
return fmt.Errorf("invalid move: %s", move)
2024-09-18 05:59:15 +00:00
}
move = strings.Replace(move, captureFrom+"x", "", 1)
2024-09-18 05:59:15 +00:00
}
2024-09-18 05:07:34 +00:00
// TODO: parse captures e.g. exd5 or Nxd5
// TODO: parse promotions
// TODO: parse checks e.g. e5+
// TODO: parse checkmates e.g. e5#
// TODO: parse O-O as kingside castle and O-O-O as queenside castle
// TODO: make sure pinned pieces cannot move
// TODO: make sure king is not in check after move
// ( this avoids moving into check and moving a piece that exposes the king to check e.g. pinned pieces )
if len(move) == 2 {
err = b.movePawn(move, captureFrom)
} else if len(move) == 3 {
piece = move[0:1]
targetPosition = move[1:3]
// collision detection
if collisionPiece, err = b.getCollision(targetPosition); err != nil {
return fmt.Errorf("invalid move %s: %v", move, err)
} else if collisionPiece != nil {
return fmt.Errorf("invalid move %s: position %s blocked by %s", move, targetPosition, collisionPiece)
}
switch strings.ToLower(piece) {
2024-09-18 04:18:07 +00:00
case "r":
err = b.moveRook(targetPosition, false)
2024-09-18 04:18:07 +00:00
case "b":
err = b.moveBishop(targetPosition, false)
2024-09-18 04:18:07 +00:00
case "n":
err = b.moveKnight(targetPosition)
2024-09-18 04:18:07 +00:00
case "q":
err = b.moveQueen(targetPosition)
2024-09-18 04:18:07 +00:00
case "k":
err = b.moveKing(targetPosition)
2024-09-18 04:18:07 +00:00
default:
err = fmt.Errorf("invalid move %s: %v", move, err)
2024-09-18 04:18:07 +00:00
}
2024-09-23 00:55:55 +00:00
} else {
return fmt.Errorf("invalid move %s: %v", move, err)
2024-09-18 02:33:58 +00:00
}
if err != nil {
return err
}
if b.turn == Light {
b.turn = Dark
} else {
b.turn = Light
}
b.moves = append(b.moves, move)
2024-09-23 04:07:34 +00:00
2024-09-18 02:33:58 +00:00
return nil
}
2024-09-18 05:59:15 +00:00
func (b *Board) movePawn(position string, captureFrom string) error {
2024-09-18 02:33:58 +00:00
var (
2024-09-25 21:30:13 +00:00
toX int
toY int
fromX int
fromY int
2024-09-18 04:01:13 +00:00
yPrev int
2024-09-18 02:33:58 +00:00
piece *Piece
err error
)
2024-09-25 21:30:13 +00:00
if toX, toY, err = getXY(position); err != nil {
2024-09-18 02:33:58 +00:00
return err
}
2024-09-18 05:59:15 +00:00
if captureFrom != "" {
2024-09-25 21:30:13 +00:00
if fromX, fromY, err = getXY(captureFrom + position[1:2]); err != nil {
2024-09-18 05:59:15 +00:00
return err
}
if b.turn == Light {
2024-09-25 21:30:13 +00:00
fromY = toY + 1
2024-09-18 05:59:15 +00:00
} else {
2024-09-25 21:30:13 +00:00
fromY = toY - 1
2024-09-18 05:59:15 +00:00
}
2024-09-25 21:30:13 +00:00
piece = b.getPiece(fromX, fromY)
2024-09-18 05:59:15 +00:00
if piece == nil || piece.Name != Pawn || piece.Color != b.turn {
// not your pawn
return fmt.Errorf("invalid capture move for pawn: %s", position)
}
2024-09-25 21:30:13 +00:00
if fromX != toX-1 && fromX != toX+1 || (b.turn == Light && fromY != toY+1) || (b.turn == Dark && fromY != toY-1) {
2024-09-18 05:59:15 +00:00
// invalid capture move
return fmt.Errorf("invalid capture move for pawn: %s", position)
}
2024-09-25 21:30:13 +00:00
b.tiles[fromX][fromY] = nil
b.tiles[toX][toY] = piece
2024-09-18 05:59:15 +00:00
return nil
}
2024-09-18 05:07:34 +00:00
// TODO: assert move is valid:
// * 2 moves from start position
// * 1 move otherwise
// * diagonal if attacking
// * no collision with other pieces
2024-09-18 02:33:58 +00:00
2024-09-18 03:20:26 +00:00
if b.turn == Light {
2024-09-25 21:30:13 +00:00
yPrev = toY + 1
2024-09-18 03:20:26 +00:00
} else {
2024-09-25 21:30:13 +00:00
yPrev = toY - 1
2024-09-18 03:20:26 +00:00
}
2024-09-25 21:30:13 +00:00
piece = b.tiles[toX][yPrev]
2024-09-18 04:01:13 +00:00
if piece != nil && piece.Name == Pawn && piece.Color == b.turn {
2024-09-25 21:30:13 +00:00
b.tiles[toX][yPrev] = nil
b.tiles[toX][toY] = piece
2024-09-18 04:01:13 +00:00
return nil
2024-09-18 02:33:58 +00:00
}
2024-09-18 04:01:13 +00:00
if b.turn == Light {
2024-09-25 21:30:13 +00:00
yPrev = toY + 2
2024-09-18 04:01:13 +00:00
} else {
2024-09-25 21:30:13 +00:00
yPrev = toY - 2
2024-09-18 03:20:26 +00:00
}
2024-09-25 21:30:13 +00:00
piece = b.tiles[toX][yPrev]
2024-09-18 04:01:13 +00:00
if piece != nil && piece.Name == Pawn && piece.Color == b.turn {
2024-09-25 21:30:13 +00:00
b.tiles[toX][yPrev] = nil
b.tiles[toX][toY] = piece
2024-09-18 04:01:13 +00:00
return nil
}
2024-09-18 03:20:26 +00:00
2024-09-18 04:01:13 +00:00
return fmt.Errorf("no pawn found that can move to %s", position)
2024-09-18 02:33:58 +00:00
}
2024-09-18 05:00:09 +00:00
func (b *Board) moveRook(position string, queen bool) error {
2024-09-18 04:49:58 +00:00
var (
x int
y int
xPrev int
yPrev int
piece *Piece
err error
)
if x, y, err = getXY(position); err != nil {
return err
}
xPrev = x
yPrev = y
for xPrev >= 0 && xPrev < 8 && yPrev >= 0 && yPrev < 8 {
xPrev++
piece = b.getPiece(xPrev, yPrev)
if piece != nil {
2024-09-18 05:00:09 +00:00
if ((!queen && piece.Name == Rook) || (queen && piece.Name == Queen)) && piece.Color == b.turn {
2024-09-18 04:49:58 +00:00
b.tiles[xPrev][yPrev] = nil
b.tiles[x][y] = piece
return nil
} else {
// direction blocked by other piece
break
}
}
}
xPrev = x
yPrev = y
for xPrev >= 0 && xPrev < 8 && yPrev >= 0 && yPrev < 8 {
yPrev--
piece = b.getPiece(xPrev, yPrev)
if piece != nil {
2024-09-18 05:00:09 +00:00
if ((!queen && piece.Name == Rook) || (queen && piece.Name == Queen)) && piece.Color == b.turn {
2024-09-18 04:49:58 +00:00
b.tiles[xPrev][yPrev] = nil
b.tiles[x][y] = piece
return nil
} else {
break
}
}
}
xPrev = x
yPrev = y
for xPrev >= 0 && xPrev < 8 && yPrev >= 0 && yPrev < 8 {
xPrev--
piece = b.getPiece(xPrev, yPrev)
if piece != nil {
2024-09-18 05:00:09 +00:00
if ((!queen && piece.Name == Rook) || (queen && piece.Name == Queen)) && piece.Color == b.turn {
2024-09-18 04:49:58 +00:00
b.tiles[xPrev][yPrev] = nil
b.tiles[x][y] = piece
return nil
} else {
break
}
}
}
xPrev = x
yPrev = y
for xPrev >= 0 && xPrev < 8 && yPrev >= 0 && yPrev < 8 {
yPrev++
piece = b.getPiece(xPrev, yPrev)
if piece != nil {
2024-09-18 05:00:09 +00:00
if ((!queen && piece.Name == Rook) || (queen && piece.Name == Queen)) && piece.Color == b.turn {
2024-09-18 04:49:58 +00:00
b.tiles[xPrev][yPrev] = nil
b.tiles[x][y] = piece
return nil
} else {
break
}
}
}
return fmt.Errorf("no rook found that can move to %s", position)
2024-09-18 04:18:07 +00:00
}
2024-09-18 05:00:09 +00:00
func (b *Board) moveBishop(position string, queen bool) error {
2024-09-18 04:44:59 +00:00
var (
x int
y int
xPrev int
yPrev int
piece *Piece
err error
)
if x, y, err = getXY(position); err != nil {
return err
}
xPrev = x
yPrev = y
for xPrev >= 0 && xPrev < 8 && yPrev >= 0 && yPrev < 8 {
xPrev++
yPrev--
piece = b.getPiece(xPrev, yPrev)
if piece != nil {
2024-09-18 05:00:09 +00:00
if ((!queen && piece.Name == Bishop) || (queen && piece.Name == Queen)) && piece.Color == b.turn {
2024-09-18 04:44:59 +00:00
b.tiles[xPrev][yPrev] = nil
b.tiles[x][y] = piece
return nil
} else {
// direction blocked by other piece
break
}
}
}
xPrev = x
yPrev = y
for xPrev >= 0 && xPrev < 8 && yPrev >= 0 && yPrev < 8 {
xPrev++
yPrev++
piece = b.getPiece(xPrev, yPrev)
if piece != nil {
2024-09-18 05:00:09 +00:00
if ((!queen && piece.Name == Bishop) || (queen && piece.Name == Queen)) && piece.Color == b.turn {
2024-09-18 04:44:59 +00:00
b.tiles[xPrev][yPrev] = nil
b.tiles[x][y] = piece
return nil
} else {
break
}
}
}
xPrev = x
yPrev = y
for xPrev >= 0 && xPrev < 8 && yPrev >= 0 && yPrev < 8 {
xPrev--
yPrev++
piece = b.getPiece(xPrev, yPrev)
if piece != nil {
2024-09-18 05:00:09 +00:00
if ((!queen && piece.Name == Bishop) || (queen && piece.Name == Queen)) && piece.Color == b.turn {
2024-09-18 04:44:59 +00:00
b.tiles[xPrev][yPrev] = nil
b.tiles[x][y] = piece
return nil
} else {
break
}
}
}
xPrev = x
yPrev = y
for xPrev >= 0 && xPrev < 8 && yPrev >= 0 && yPrev < 8 {
xPrev--
yPrev--
piece = b.getPiece(xPrev, yPrev)
if piece != nil {
2024-09-18 05:00:09 +00:00
if ((!queen && piece.Name == Bishop) || (queen && piece.Name == Queen)) && piece.Color == b.turn {
2024-09-18 04:44:59 +00:00
b.tiles[xPrev][yPrev] = nil
b.tiles[x][y] = piece
return nil
} else {
break
}
}
}
return fmt.Errorf("no bishop found that can move to %s", position)
2024-09-18 04:18:07 +00:00
}
func (b *Board) moveKnight(position string) error {
var (
x int
y int
xPrev int
yPrev int
piece *Piece
err error
)
if x, y, err = getXY(position); err != nil {
return err
}
xPrev = x + 1
yPrev = y - 2
2024-09-18 04:23:51 +00:00
piece = b.getPiece(xPrev, yPrev)
2024-09-18 04:18:07 +00:00
if piece != nil && piece.Name == Knight && piece.Color == b.turn {
b.tiles[xPrev][yPrev] = nil
b.tiles[x][y] = piece
return nil
}
xPrev = x + 2
yPrev = y - 1
2024-09-18 04:23:51 +00:00
piece = b.getPiece(xPrev, yPrev)
2024-09-18 04:18:07 +00:00
if piece != nil && piece.Name == Knight && piece.Color == b.turn {
b.tiles[xPrev][yPrev] = nil
b.tiles[x][y] = piece
return nil
}
xPrev = x + 2
yPrev = y + 1
2024-09-18 04:23:51 +00:00
piece = b.getPiece(xPrev, yPrev)
2024-09-18 04:18:07 +00:00
if piece != nil && piece.Name == Knight && piece.Color == b.turn {
b.tiles[xPrev][yPrev] = nil
b.tiles[x][y] = piece
return nil
}
xPrev = x + 1
yPrev = y + 2
2024-09-18 04:23:51 +00:00
piece = b.getPiece(xPrev, yPrev)
2024-09-18 04:18:07 +00:00
if piece != nil && piece.Name == Knight && piece.Color == b.turn {
b.tiles[xPrev][yPrev] = nil
b.tiles[x][y] = piece
return nil
}
xPrev = x - 1
yPrev = y + 2
2024-09-18 04:23:51 +00:00
piece = b.getPiece(xPrev, yPrev)
2024-09-18 04:18:07 +00:00
if piece != nil && piece.Name == Knight && piece.Color == b.turn {
b.tiles[xPrev][yPrev] = nil
b.tiles[x][y] = piece
return nil
}
xPrev = x - 2
yPrev = y + 1
2024-09-18 04:23:51 +00:00
piece = b.getPiece(xPrev, yPrev)
2024-09-18 04:18:07 +00:00
if piece != nil && piece.Name == Knight && piece.Color == b.turn {
b.tiles[xPrev][yPrev] = nil
b.tiles[x][y] = piece
return nil
}
xPrev = x - 2
yPrev = y - 1
2024-09-18 04:23:51 +00:00
piece = b.getPiece(xPrev, yPrev)
2024-09-18 04:18:07 +00:00
if piece != nil && piece.Name == Knight && piece.Color == b.turn {
b.tiles[xPrev][yPrev] = nil
b.tiles[x][y] = piece
return nil
}
xPrev = x - 1
yPrev = y - 2
2024-09-18 04:23:51 +00:00
piece = b.getPiece(xPrev, yPrev)
2024-09-18 04:18:07 +00:00
if piece != nil && piece.Name == Knight && piece.Color == b.turn {
b.tiles[xPrev][yPrev] = nil
b.tiles[x][y] = piece
return nil
}
return fmt.Errorf("no knight found that can move to %s", position)
}
func (b *Board) moveQueen(position string) error {
2024-09-18 05:00:09 +00:00
var (
err error
)
if err = b.moveBishop(position, true); err == nil {
return nil
}
if err = b.moveRook(position, true); err == nil {
return nil
}
return fmt.Errorf("no queen found that can move to %s", position)
2024-09-18 04:18:07 +00:00
}
func (b *Board) moveKing(position string) error {
2024-09-18 05:07:34 +00:00
// TODO: implement king moves
2024-09-18 04:18:07 +00:00
return nil
}
2024-09-18 01:15:24 +00:00
func (b *Board) mustSetPiece(name PieceName, color Color, position string) {
if err := b.SetPiece(name, color, position); err != nil {
log.Fatalf("cannot set piece %s: %v", name, err)
}
}
2024-09-18 02:33:58 +00:00
func (b *Board) At(position string) *Piece {
var (
x int
y int
err error
)
if x, y, err = getXY(position); err != nil {
return nil
}
return b.tiles[x][y]
}
2024-09-18 01:15:24 +00:00
func getXY(position string) (int, int, error) {
var (
posX rune
posY rune
x int
y int
)
runes := []rune(position)
posX = runes[0]
posY = runes[1]
2024-09-25 21:22:42 +00:00
if posX < 'a' || posX > 'h' {
return -1, -1, fmt.Errorf("invalid move: %s", position)
2024-09-18 01:15:24 +00:00
}
2024-09-25 21:22:42 +00:00
if posY < '1' || posY > '8' {
return -1, -1, fmt.Errorf("invalid move: %s", position)
2024-09-18 01:15:24 +00:00
}
// image origin (0,0) is at top-left corner (a8)
x = int(posX - 'a')
y = int('8' - posY)
return x, y, nil
}
2024-09-18 04:23:51 +00:00
func (b *Board) getPiece(x int, y int) *Piece {
if x < 0 || x >= 8 || y < 0 || y >= 8 {
return nil
}
return b.tiles[x][y]
}
func (b *Board) getCollision(position string) (*Piece, error) {
var (
x, y int
p *Piece
collision bool
err error
)
if x, y, err = getXY(position); err != nil {
return nil, err
}
p = b.getPiece(x, y)
// check if position is occupied by own piece
collision = p != nil && p.Color == b.turn
if collision {
return p, nil
}
// TODO: check if path is blocked by some piece
return nil, nil
}
2024-09-18 01:15:24 +00:00
func getTileColor(x, y int) Color {
if x%2 == y%2 {
return Light
} else {
return Dark
}
}
2024-09-23 04:25:23 +00:00
func flipImage(img image.Image) *image.RGBA {
bounds := img.Bounds()
flipped := image.NewRGBA(bounds)
// Flip the image vertically by reversing the Y coordinate
for y := bounds.Min.Y; y < bounds.Max.Y; y++ {
for x := bounds.Min.X; x < bounds.Max.X; x++ {
flipped.Set(x, bounds.Max.Y-y-1, img.At(x, y))
}
}
return flipped
}