v0.7.0 cli client with readline added, directory structure updated

This commit is contained in:
2025-11-13 08:55:06 -05:00
parent 52868af4ea
commit 6bdc061508
52 changed files with 2260 additions and 157 deletions

View File

@ -0,0 +1,120 @@
// FILE: lixenwraith/chess/internal/server/board/board.go
package board
import (
"fmt"
"strings"
"chess/internal/server/core"
)
const (
StartingFEN = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"
)
type Board struct {
squares [8][8]byte
turn core.Color
castling string
enPassant string
halfmove int
fullmove int
}
// FromFEN creates a Board from a FEN string with validation
func ParseFEN(fen string) (*Board, error) {
parts := strings.Fields(fen)
if len(parts) != 6 {
return nil, fmt.Errorf("invalid FEN: expected 6 parts, got %d", len(parts))
}
b := &Board{}
// Parse board
ranks := strings.Split(parts[0], "/")
if len(ranks) != 8 {
return nil, fmt.Errorf("invalid FEN: expected 8 ranks")
}
for r := 0; r < 8; r++ {
file := 0
for _, ch := range ranks[r] {
if ch >= '1' && ch <= '8' {
file += int(ch - '0')
} else {
if file >= 8 {
return nil, fmt.Errorf("invalid FEN: too many pieces in rank %d", r+1)
}
b.squares[r][file] = byte(ch)
file++
}
}
if file != 8 {
return nil, fmt.Errorf("invalid FEN: rank %d has %d files", r+1, file)
}
}
// Parse game state with validation
if len(parts[1]) != 1 {
return nil, fmt.Errorf("invalid FEN: turn must be 'w' or 'b'")
}
switch parts[1] {
case "w":
b.turn = core.ColorWhite
case "b":
b.turn = core.ColorBlack
default:
return nil, fmt.Errorf("invalid FEN: turn must be 'w' or 'b'")
}
b.castling = parts[2]
b.enPassant = parts[3]
if _, err := fmt.Sscanf(parts[4], "%d", &b.halfmove); err != nil {
return nil, fmt.Errorf("invalid FEN: halfmove counter")
}
if _, err := fmt.Sscanf(parts[5], "%d", &b.fullmove); err != nil {
return nil, fmt.Errorf("invalid FEN: fullmove counter")
}
return b, nil
}
// ToASCII creates an ASCII representation of the board
func (b *Board) ToASCII() string {
var sb strings.Builder
sb.WriteString(" a b c d e f g h\n")
for r := 0; r < 8; r++ {
sb.WriteString(fmt.Sprintf("%d ", 8-r))
for f := 0; f < 8; f++ {
square := fmt.Sprintf("%c%c", 'a'+f, '8'-r)
piece := b.GetPieceAt(square)
if piece == 0 {
sb.WriteString(". ")
} else {
sb.WriteString(fmt.Sprintf("%c ", piece))
}
}
sb.WriteString(fmt.Sprintf("%d\n", 8-r))
}
sb.WriteString(" a b c d e f g h")
return sb.String()
}
func (b *Board) Turn() core.Color {
return b.turn
}
func (b *Board) GetPieceAt(square string) byte {
if len(square) != 2 {
return 0
}
if square[0] < 'a' || square[0] > 'h' || square[1] < '1' || square[1] > '8' {
return 0
}
file := square[0] - 'a'
rank := '8' - square[1]
return b.squares[rank][file]
}