Draw initial board
Before Width: | Height: | Size: 26 KiB |
Before Width: | Height: | Size: 26 KiB |
Before Width: | Height: | Size: 40 KiB |
Before Width: | Height: | Size: 40 KiB |
Before Width: | Height: | Size: 53 KiB |
Before Width: | Height: | Size: 53 KiB |
After Width: | Height: | Size: 30 KiB |
Before Width: | Height: | Size: 46 KiB |
Before Width: | Height: | Size: 46 KiB |
After Width: | Height: | Size: 27 KiB |
Before Width: | Height: | Size: 35 KiB |
Before Width: | Height: | Size: 36 KiB |
Before Width: | Height: | Size: 44 KiB |
Before Width: | Height: | Size: 45 KiB |
Before Width: | Height: | Size: 7.9 KiB |
Before Width: | Height: | Size: 7.9 KiB |
Before Width: | Height: | Size: 28 KiB |
Before Width: | Height: | Size: 28 KiB |
Before Width: | Height: | Size: 52 KiB |
Before Width: | Height: | Size: 52 KiB |
Before Width: | Height: | Size: 59 KiB |
Before Width: | Height: | Size: 59 KiB |
Before Width: | Height: | Size: 11 KiB |
Before Width: | Height: | Size: 11 KiB |
Before Width: | Height: | Size: 9.7 KiB |
Before Width: | Height: | Size: 9.9 KiB |
|
@ -0,0 +1,153 @@
|
|||
package chess
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"image"
|
||||
"image/draw"
|
||||
"image/png"
|
||||
"log"
|
||||
"os"
|
||||
)
|
||||
|
||||
type Board struct {
|
||||
tiles [8][8]*Piece
|
||||
}
|
||||
|
||||
func NewBoard() *Board {
|
||||
board := &Board{}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
func (b *Board) Save(filename string) error {
|
||||
var (
|
||||
file *os.File
|
||||
img *image.RGBA
|
||||
piece *Piece
|
||||
bg *image.Uniform
|
||||
rect image.Rectangle
|
||||
p = image.Point{0, 0}
|
||||
err error
|
||||
)
|
||||
|
||||
if file, err = os.Create(filename); err != nil {
|
||||
return err
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
img = image.NewRGBA(image.Rect(0, 0, 1024, 1024))
|
||||
|
||||
for yi := 0; yi < 8; yi++ {
|
||||
for xi := 0; xi < 8; xi++ {
|
||||
rect = image.Rect(xi*128, yi*128, (xi*128)+128, (yi*128)+128)
|
||||
bg = image.NewUniform(getTileColor(xi, yi))
|
||||
draw.Draw(img, rect, bg, p, draw.Src)
|
||||
|
||||
piece = b.tiles[xi][yi]
|
||||
if piece != nil {
|
||||
draw.Draw(img, rect, piece.Image, p, draw.Over)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return png.Encode(file, img)
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
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]
|
||||
|
||||
if posX < 'a' && posX > 'h' {
|
||||
return -1, -1, fmt.Errorf("invalid posX: %s", position)
|
||||
}
|
||||
|
||||
if posY < '1' && posY > '8' {
|
||||
return -1, -1, fmt.Errorf("invalid posY: %s", position)
|
||||
}
|
||||
|
||||
// image origin (0,0) is at top-left corner (a8)
|
||||
x = int(posX - 'a')
|
||||
y = int('8' - posY)
|
||||
|
||||
return x, y, nil
|
||||
}
|
||||
|
||||
func getTileColor(x, y int) Color {
|
||||
if x%2 == y%2 {
|
||||
return Light
|
||||
} else {
|
||||
return Dark
|
||||
}
|
||||
}
|
|
@ -0,0 +1,73 @@
|
|||
package chess
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"image"
|
||||
"image/color"
|
||||
"image/png"
|
||||
"os"
|
||||
|
||||
"golang.org/x/image/draw"
|
||||
)
|
||||
|
||||
type Piece struct {
|
||||
Name PieceName
|
||||
Color color.Color
|
||||
Image image.Image
|
||||
}
|
||||
|
||||
type PieceName string
|
||||
|
||||
const (
|
||||
Pawn PieceName = "p"
|
||||
Knight PieceName = "n"
|
||||
Bishop PieceName = "b"
|
||||
Rook PieceName = "r"
|
||||
Queen PieceName = "q"
|
||||
King PieceName = "k"
|
||||
)
|
||||
|
||||
type Color color.Color
|
||||
|
||||
var (
|
||||
Light Color = color.RGBA{240, 217, 181, 255}
|
||||
Dark Color = color.RGBA{181, 136, 99, 255}
|
||||
)
|
||||
|
||||
func NewPiece(name PieceName, color Color) (*Piece, error) {
|
||||
var (
|
||||
colorSuffix string
|
||||
path string
|
||||
file *os.File
|
||||
img image.Image
|
||||
dst *image.RGBA
|
||||
err error
|
||||
)
|
||||
|
||||
colorSuffix = "l"
|
||||
if color == Light {
|
||||
colorSuffix = "l"
|
||||
} else if color == Dark {
|
||||
colorSuffix = "d"
|
||||
} else {
|
||||
return nil, fmt.Errorf("invalid color: %v", color)
|
||||
}
|
||||
|
||||
path = fmt.Sprintf("assets/1024px-Chess_%s%st45.svg.png", name, colorSuffix)
|
||||
|
||||
if file, err = os.Open(path); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
if img, err = png.Decode(file); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// source image for each piece is 1024x1024 and board is 8x8
|
||||
// so we need to scale each piece down to 128x128 (1024/8)
|
||||
dst = image.NewRGBA(image.Rect(0, 0, 128, 128))
|
||||
draw.CatmullRom.Scale(dst, dst.Rect, img, img.Bounds(), draw.Over, nil)
|
||||
|
||||
return &Piece{Name: name, Color: color, Image: dst}, nil
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
module github.com/ekzyis/sn-chess
|
||||
|
||||
go 1.23.0
|
||||
|
||||
require golang.org/x/image v0.20.0
|
|
@ -0,0 +1,2 @@
|
|||
golang.org/x/image v0.20.0 h1:7cVCUjQwfL18gyBJOmYvptfSHS8Fb3YUDtfLIZ7Nbpw=
|
||||
golang.org/x/image v0.20.0/go.mod h1:0a88To4CYVBAHp5FXJm8o7QbUl37Vd85ply1vyD8auM=
|