Categorygithub.com/thewizardplusplus/go-chess-models
repositorypackage
1.9.0
Repository: https://github.com/thewizardplusplus/go-chess-models.git
Documentation: pkg.go.dev

# Packages

No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author

# README

go-chess-models

GoDoc Go Report Card Build Status codecov

The library that implements checking and generating of chess moves.

Disclaimer: this library was written directly on an Android smartphone with the AnGoIde IDE.

Features

  • representing the board:
    • as an associative array of pieces with their positions as keys;
    • as a plain array of pieces with exact correspondence array indices to piece positions;
    • as a set of integers corresponding to a particular combination of piece color and type, and where each bit corresponds to a particular piece position (so-called a bitboard);
  • immutable applicating moves to the board via copying the latter;
  • checkings of moves:
    • universal;
    • individual for all types of pieces;
  • generating moves via filtering from all possible ones;
  • move restrictions (abandoned moves):
    • pawn double-move;
    • en passant capture;
    • promotion;
    • castling;
  • perft function;
  • using an abstraction of a piece;
  • Forsyth-Edwards Notation:
    • parsing:
      • of a position;
      • of a move;
      • of a piece kind;
      • of a piece color;
      • of a board;
    • serialization:
      • of a position;
      • of a move;
      • of a piece kind;
      • of a piece color;
      • of a board;
  • utilities:
    • utility for counting all possible moves (based on the perft function);
    • utility for generating all possible chess moves;
    • utility for comparing the generation of all possible chess moves by different board representations.

Installation

$ go get github.com/thewizardplusplus/go-chess-models

Examples

boards.MapBoard.CheckMove():

package main

import (
	"fmt"

	"github.com/thewizardplusplus/go-chess-models/boards"
	"github.com/thewizardplusplus/go-chess-models/common"
	"github.com/thewizardplusplus/go-chess-models/pieces"
)

func main() {
	board := boards.NewMapBoard(common.Size{Width: 5, Height: 5}, []common.Piece{
		pieces.NewRook(common.Black, common.Position{File: 2, Rank: 2}),
		pieces.NewBishop(common.White, common.Position{File: 3, Rank: 3}),
	})

	moveOne := common.Move{
		Start:  common.Position{File: 2, Rank: 2},
		Finish: common.Position{File: 3, Rank: 3},
	}
	fmt.Printf("%+v: %v\n", moveOne, board.CheckMove(moveOne))

	moveTwo := common.Move{
		Start:  common.Position{File: 3, Rank: 3},
		Finish: common.Position{File: 2, Rank: 2},
	}
	fmt.Printf("%+v: %v\n", moveTwo, board.CheckMove(moveTwo))

	// Output:
	// {Start:{File:2 Rank:2} Finish:{File:3 Rank:3}}: illegal move
	// {Start:{File:3 Rank:3} Finish:{File:2 Rank:2}}: <nil>
}

boards.MapBoard.ApplyMove():

package main

import (
	"fmt"
	"sort"

	"github.com/thewizardplusplus/go-chess-models/boards"
	"github.com/thewizardplusplus/go-chess-models/common"
	"github.com/thewizardplusplus/go-chess-models/pieces"
)

type ByPosition []common.Piece

func (group ByPosition) Len() int {
	return len(group)
}

func (group ByPosition) Swap(i int, j int) {
	group[i], group[j] = group[j], group[i]
}

func (group ByPosition) Less(i int, j int) bool {
	a, b := group[i].Position(), group[j].Position()
	if a.File == b.File {
		return a.Rank < b.Rank
	}

	return a.File < b.File
}

func main() {
	board := boards.NewMapBoard(common.Size{Width: 5, Height: 5}, []common.Piece{
		pieces.NewRook(common.Black, common.Position{File: 2, Rank: 2}),
		pieces.NewBishop(common.White, common.Position{File: 3, Rank: 3}),
	})
	pieces := board.Pieces()
	sort.Sort(ByPosition(pieces))
	fmt.Printf("%+v\n", pieces)

	updatedBoard := board.ApplyMove(common.Move{
		Start:  common.Position{File: 3, Rank: 3},
		Finish: common.Position{File: 2, Rank: 2},
	})
	updatedPieces := updatedBoard.Pieces()
	sort.Sort(ByPosition(updatedPieces))
	fmt.Printf("%+v\n", updatedPieces)

	// Output:
	// [{Base:{kind:2 color:0 position:{File:2 Rank:2}}} {Base:{kind:3 color:1 position:{File:3 Rank:3}}}]
	// [{Base:{kind:3 color:1 position:{File:2 Rank:2}}}]
}

chessmodels.MoveGenerator.MovesForColor():

package main

import (
	"fmt"
	"sort"

	models "github.com/thewizardplusplus/go-chess-models"
	"github.com/thewizardplusplus/go-chess-models/boards"
	"github.com/thewizardplusplus/go-chess-models/common"
	"github.com/thewizardplusplus/go-chess-models/pieces"
)

func main() {
	board := boards.NewMapBoard(common.Size{Width: 5, Height: 5}, []common.Piece{
		pieces.NewRook(common.Black, common.Position{File: 2, Rank: 2}),
		pieces.NewKnight(common.White, common.Position{File: 3, Rank: 3}),
		pieces.NewPawn(common.White, common.Position{File: 4, Rank: 3}),
	})

	var generator models.MoveGenerator
	moves, _ := generator.MovesForColor(board, common.White)

	// sorting only by the final point will be sufficient for the reproducibility
	// of this example
	sort.Slice(moves, func(i int, j int) bool {
		a, b := moves[i].Finish, moves[j].Finish
		if a.File == b.File {
			return a.Rank < b.Rank
		}

		return a.File < b.File
	})

	for _, move := range moves {
		fmt.Printf("%+v\n", move)
	}

	// Output:
	// {Start:{File:3 Rank:3} Finish:{File:1 Rank:2}}
	// {Start:{File:3 Rank:3} Finish:{File:1 Rank:4}}
	// {Start:{File:3 Rank:3} Finish:{File:2 Rank:1}}
	// {Start:{File:3 Rank:3} Finish:{File:4 Rank:1}}
	// {Start:{File:4 Rank:3} Finish:{File:4 Rank:4}}
}

uci.DecodeMove():

package main

import (
	"fmt"

	"github.com/thewizardplusplus/go-chess-models/encoding/uci"
)

func main() {
	move, _ := uci.DecodeMove("d4c3")
	fmt.Printf("%+v\n", move)

	// Output: {Start:{File:3 Rank:3} Finish:{File:2 Rank:2}}
}

uci.EncodeMove():

package main

import (
	"fmt"

	"github.com/thewizardplusplus/go-chess-models/common"
	"github.com/thewizardplusplus/go-chess-models/encoding/uci"
)

func main() {
	move := uci.EncodeMove(common.Move{
		Start:  common.Position{File: 3, Rank: 3},
		Finish: common.Position{File: 2, Rank: 2},
	})
	fmt.Printf("%v\n", move)

	// Output: d4c3
}

uci.DecodePieceStorage():

package main

import (
	"fmt"
	"sort"

	"github.com/thewizardplusplus/go-chess-models/boards"
	"github.com/thewizardplusplus/go-chess-models/common"
	"github.com/thewizardplusplus/go-chess-models/encoding/uci"
	"github.com/thewizardplusplus/go-chess-models/pieces"
)

type ByPosition []common.Piece

func (group ByPosition) Len() int {
	return len(group)
}

func (group ByPosition) Swap(i int, j int) {
	group[i], group[j] = group[j], group[i]
}

func (group ByPosition) Less(i int, j int) bool {
	a, b := group[i].Position(), group[j].Position()
	if a.File == b.File {
		return a.Rank < b.Rank
	}

	return a.File < b.File
}

func main() {
	const fen = "8/8/8/8/3B4/2r5/8/8"
	storage, _ := uci.DecodePieceStorage(fen, pieces.NewPiece, boards.NewMapBoard)
	pieces := storage.Pieces()
	sort.Sort(ByPosition(pieces))
	fmt.Printf("%+v\n", pieces)

	// Output: [{Base:{kind:2 color:0 position:{File:2 Rank:2}}} {Base:{kind:3 color:1 position:{File:3 Rank:3}}}]
}

uci.EncodePieceStorage():

package main

import (
	"fmt"

	"github.com/thewizardplusplus/go-chess-models/boards"
	"github.com/thewizardplusplus/go-chess-models/common"
	"github.com/thewizardplusplus/go-chess-models/encoding/uci"
	"github.com/thewizardplusplus/go-chess-models/pieces"
)

func main() {
	board := boards.NewMapBoard(common.Size{Width: 5, Height: 5}, []common.Piece{
		pieces.NewRook(common.Black, common.Position{File: 2, Rank: 2}),
		pieces.NewBishop(common.White, common.Position{File: 3, Rank: 3}),
	})
	fen := uci.EncodePieceStorage(board)
	fmt.Printf("%v\n", fen)

	// Output: 5/3B1/2r2/5/5
}

Benchmarks

The chessmodels.Perft() function using the boards.MapBoard structure:

BenchmarkPerft/MapBoard/initial/1Ply-8         	     486	   2239365 ns/op	  507723 B/op	   13638 allocs/op
BenchmarkPerft/MapBoard/initial/2Ply-8         	      38	  27071904 ns/op	 6253672 B/op	  165159 allocs/op
BenchmarkPerft/MapBoard/initial/3Ply-8         	       3	 394039297 ns/op	91006645 B/op	 2399815 allocs/op
BenchmarkPerft/MapBoard/kiwipete/1Ply-8        	     150	   7844416 ns/op	 1959120 B/op	   47637 allocs/op
BenchmarkPerft/MapBoard/kiwipete/2Ply-8        	       4	 311539966 ns/op	80169034 B/op	 1892731 allocs/op

The chessmodels.Perft() function used the boards.SliceBoard structure:

BenchmarkPerft/SliceBoard/initial/1Ply-8       	     810	   1419572 ns/op	   684016 B/op	   13646 allocs/op
BenchmarkPerft/SliceBoard/initial/2Ply-8       	      68	  17883575 ns/op	  8331384 B/op	  165074 allocs/op
BenchmarkPerft/SliceBoard/initial/3Ply-8       	       4	 253594389 ns/op	121325578 B/op	 2399248 allocs/op
BenchmarkPerft/SliceBoard/kiwipete/1Ply-8      	     220	   5340912 ns/op	  2583824 B/op	   47672 allocs/op
BenchmarkPerft/SliceBoard/kiwipete/2Ply-8      	       5	 208109435 ns/op	103011680 B/op	 1895999 allocs/op

The chessmodels.Perft() function used the boards.BitBoard structure:

BenchmarkPerft/BitBoard/initial/1Ply-8         	     284	   4036298 ns/op	   958048 B/op	   35888 allocs/op
BenchmarkPerft/BitBoard/initial/2Ply-8         	      20	  54032966 ns/op	 11627104 B/op	  433592 allocs/op
BenchmarkPerft/BitBoard/initial/3Ply-8         	       2	 708741154 ns/op	169201536 B/op	 6300499 allocs/op
BenchmarkPerft/BitBoard/kiwipete/1Ply-8        	      74	  14372268 ns/op	  3546305 B/op	  124803 allocs/op
BenchmarkPerft/BitBoard/kiwipete/2Ply-8        	       2	 637371490 ns/op	138270896 B/op	 4866744 allocs/op

Utilities

  • go-chess-perft — utility for counting all possible moves (based on the perft function)
  • go-chess-moves — utility for generating all possible chess moves
  • go-chess-comparator — utility for comparing the generation of all possible chess moves by different board representations

License

The MIT License (MIT)

Copyright © 2019, 2022-2023 thewizardplusplus