Browse Source

Initial commit from fork

master
Gregory Rudolph 4 years ago
commit
369af2097d
Signed by: rudi
GPG Key ID: EF64F3CBD1A1EBDD
  1. 33
      .gitignore
  2. 22
      LICENSE
  3. 396
      README.md
  4. 135
      assets/valid_notation_tests.json
  5. 61
      bitboard.go
  6. 15
      bitboard_postbits.go
  7. 42
      bitboard_prebits.go
  8. 55
      bitboard_test.go
  9. 424
      board.go
  10. 90
      board_test.go
  11. 50
      doc.go
  12. 336
      engine.go
  13. BIN
      example.png
  14. 155
      fen.go
  15. 54
      fen_test.go
  16. 402
      game.go
  17. 292
      game_test.go
  18. 5
      go.mod
  19. 2
      go.sum
  20. BIN
      image/.DS_Store
  21. 84
      image/README.md
  22. BIN
      image/example.png
  23. 534
      image/example.svg
  24. 168
      image/image.go
  25. 48
      image/image_test.go
  26. 513
      image/internal/bindata.go
  27. 15
      image/internal/pieces/bB.svg
  28. 23
      image/internal/pieces/bK.svg
  29. 20
      image/internal/pieces/bN.svg
  30. 5
      image/internal/pieces/bP.svg
  31. 32
      image/internal/pieces/bQ.svg
  32. 37
      image/internal/pieces/bR.svg
  33. 15
      image/internal/pieces/wB.svg
  34. 25
      image/internal/pieces/wK.svg
  35. 17
      image/internal/pieces/wN.svg
  36. 5
      image/internal/pieces/wP.svg
  37. 31
      image/internal/pieces/wQ.svg
  38. 23
      image/internal/pieces/wR.svg
  39. 534
      image/test.svg
  40. 72
      move.go
  41. 302
      move_test.go
  42. 224
      notation.go
  43. 106
      notation_test.go
  44. 167
      pgn.go
  45. 114
      pgn_test.go
  46. 192
      piece.go
  47. 221
      position.go
  48. 156
      square.go
  49. 16
      stringer.go
  50. 220
      vendor/github.com/ajstarks/svgo/LICENSE
  51. 3
      vendor/github.com/ajstarks/svgo/LICENSE-link.txt
  52. 727
      vendor/github.com/ajstarks/svgo/README.markdown
  53. 126
      vendor/github.com/ajstarks/svgo/doc.go
  54. BIN
      vendor/github.com/ajstarks/svgo/gophercolor128x128.png
  55. 39
      vendor/github.com/ajstarks/svgo/newsvg
  56. 1055
      vendor/github.com/ajstarks/svgo/svg.go
  57. BIN
      vendor/github.com/ajstarks/svgo/svgdef.pdf
  58. BIN
      vendor/github.com/ajstarks/svgo/svgdef.png
  59. 395
      vendor/github.com/ajstarks/svgo/svgdef.svg
  60. 3
      vendor/modules.txt

33
.gitignore vendored

@ -0,0 +1,33 @@ @@ -0,0 +1,33 @@
# Compiled Object files, Static and Dynamic libs (Shared Objects)
*.o
*.a
*.so
# Folders
_obj
_test
# Architecture specific extensions/prefixes
*.[568vq]
[568vq].out
*.cgo1.go
*.cgo2.c
_cgo_defun.c
_cgo_gotypes.go
_cgo_export.*
_testmain.go
*.exe
*.test
*.prof
# VS Code
.vscode
# Benchmarking
cpu.out
# testing directories
cmd

22
LICENSE

@ -0,0 +1,22 @@ @@ -0,0 +1,22 @@
The MIT License (MIT)
Copyright (c) 2015 Logan Spears
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

396
README.md

@ -0,0 +1,396 @@ @@ -0,0 +1,396 @@
# chess
[![GoDoc](http://img.shields.io/badge/go-documentation-blue.svg?style=flat-square)](https://godoc.org/github.com/notnil/chess)
[![Coverage Status](https://coveralls.io/repos/notnil/chess/badge.svg?branch=master&service=github)](https://coveralls.io/github/notnil/chess?branch=master)
[![Go Report Card](https://goreportcard.com/badge/notnil/chess)](https://goreportcard.com/report/notnil/chess)
[![License](http://img.shields.io/badge/license-mit-blue.svg?style=flat-square)](https://raw.githubusercontent.com/notnil/chess/master/LICENSE)
## Introduction
Chess is a go library which provides common chess utilities such as move generation, turn management, checkmate detection, PGN encoding, and others. It is well tested and optimized for performance.
## Installation
The package can be installed using "go get".
```bash
go get -u github.com/notnil/chess
```
## Usage
### Movement
Chess exposes two ways of moving: valid move generation and notation parsing. Valid moves are calculated from the current position and are returned from the ValidMoves method. Even if the client isn't a go program (e.g. a web app) the list of moves can be serialized into their string representation and supplied to the client. Once a move is selected the MoveStr method can be used to parse the selected move's string.
#### Valid Moves
Valid moves generated from the game's current position:
```go
game := chess.NewGame()
moves := game.ValidMoves()
game.Move(moves[0])
fmt.Println(moves[0]) // b1a3
```
#### Parse Notation
Game's MoveStr method accepts string input using the default Algebraic Notation:
```go
game := chess.NewGame()
if err := game.MoveStr("e4"); err != nil {
// handle error
}
```
### Outcome
The outcome of the match is calculated automatically from the inputted moves if possible. Draw agreements, resignations, and other human initiated outcomes can be inputted as well.
#### Checkmate
Black wins by checkmate (Fool's Mate):
```go
game := chess.NewGame()
game.MoveStr("f3")
game.MoveStr("e6")
game.MoveStr("g4")
game.MoveStr("Qh4")
fmt.Println(game.Outcome()) // 0-1
fmt.Println(game.Method()) // Checkmate
/*
A B C D E F G H
8♜ ♞ ♝ - ♚ ♝ ♞ ♜
7♟ ♟ ♟ ♟ - ♟ ♟ ♟
6- - - - ♟ - - -
5- - - - - - - -
4- - - - - - ♙ ♛
3- - - - - ♙ - -
2♙ ♙ ♙ ♙ ♙ - - ♙
1♖ ♘ ♗ ♕ ♔ ♗ ♘ ♖
*/
```
#### Stalemate
Black king has no safe move:
```go
fenStr := "k1K5/8/8/8/8/8/8/1Q6 w - - 0 1"
fen, _ := chess.FEN(fenStr)
game := chess.NewGame(fen)
game.MoveStr("Qb6")
fmt.Println(game.Outcome()) // 1/2-1/2
fmt.Println(game.Method()) // Stalemate
/*
A B C D E F G H
8♚ - ♔ - - - - -
7- - - - - - - -
6- ♕ - - - - - -
5- - - - - - - -
4- - - - - - - -
3- - - - - - - -
2- - - - - - - -
1- - - - - - - -
*/
```
#### Resignation
Black resigns and white wins:
```go
game := chess.NewGame()
game.MoveStr("f3")
game.Resign(chess.Black)
fmt.Println(game.Outcome()) // 1-0
fmt.Println(game.Method()) // Resignation
```
#### Draw Offer
Draw by mutual agreement:
```go
game := chess.NewGame()
game.Draw(chess.DrawOffer)
fmt.Println(game.Outcome()) // 1/2-1/2
fmt.Println(game.Method()) // DrawOffer
```
#### Threefold Repetition
[Threefold repetition](https://en.wikipedia.org/wiki/Threefold_repetition) occurs when the position repeats three times (not necessarily in a row). If this occurs both players have the option of taking a draw, but aren't required until Fivefold Repetition.
```go
game := chess.NewGame()
moves := []string{"Nf3", "Nf6", "Ng1", "Ng8", "Nf3", "Nf6", "Ng1", "Ng8"}
for _, m := range moves {
game.MoveStr(m)
}
fmt.Println(game.EligibleDraws()) // [DrawOffer ThreefoldRepetition]
```
#### Fivefold Repetition
According to the [FIDE Laws of Chess](http://www.fide.com/component/handbook/?id=171&view=article) if a position repeats five times then the game is drawn automatically.
```go
game := chess.NewGame()
moves := []string{
"Nf3", "Nf6", "Ng1", "Ng8",
"Nf3", "Nf6", "Ng1", "Ng8",
"Nf3", "Nf6", "Ng1", "Ng8",
"Nf3", "Nf6", "Ng1", "Ng8",
}
for _, m := range moves {
game.MoveStr(m)
}
fmt.Println(game.Outcome()) // 1/2-1/2
fmt.Println(game.Method()) // FivefoldRepetition
```
#### Fifty Move Rule
[Fifty-move rule](https://en.wikipedia.org/wiki/Fifty-move_rule) allows either player to claim a draw if no capture has been made and no pawn has been moved in the last fifty moves.
```go
fen, _ := chess.FEN("2r3k1/1q1nbppp/r3p3/3pP3/pPpP4/P1Q2N2/2RN1PPP/2R4K b - b3 100 23")
game := chess.NewGame(fen)
game.Draw(chess.FiftyMoveRule)
fmt.Println(game.Outcome()) // 1/2-1/2
fmt.Println(game.Method()) // FiftyMoveRule
```
#### Seventy Five Move Rule
According to [FIDE Laws of Chess Rule 9.6b](http://www.fide.com/component/handbook/?id=171&view=article) if 75 consecutive moves have been made without movement of any pawn or any capture, the game is drawn unless the last move was checkmate.
```go
fen, _ := chess.FEN("2r3k1/1q1nbppp/r3p3/3pP3/pPpP4/P1Q2N2/2RN1PPP/2R4K b - b3 149 23")
game := chess.NewGame(fen)
game.MoveStr("Kf8")
fmt.Println(game.Outcome()) // 1/2-1/2
fmt.Println(game.Method()) // SeventyFiveMoveRule
```
#### Insufficient Material
[Impossibility of checkmate](https://en.wikipedia.org/wiki/Draw_%28chess%29#Draws_in_all_games), or insufficient material, results when neither white or black has the pieces remaining to checkmate the opponent.
```go
fen, _ := chess.FEN("8/2k5/8/8/8/3K4/8/8 w - - 1 1")
game := chess.NewGame(fen)
fmt.Println(game.Outcome()) // 1/2-1/2
fmt.Println(game.Method()) // InsufficientMaterial
```
### PGN
[PGN](https://en.wikipedia.org/wiki/Portable_Game_Notation), or Portable Game Notation, is the most common serialization format for chess matches. PGNs include move history and metadata about the match. Chess includes the ability to read and write the PGN format.
#### Example PGN
```
[Event "F/S Return Match"]
[Site "Belgrade, Serbia JUG"]
[Date "1992.11.04"]
[Round "29"]
[White "Fischer, Robert J."]
[Black "Spassky, Boris V."]
[Result "1/2-1/2"]
1. e4 e5 2. Nf3 Nc6 3. Bb5 a6 {This opening is called the Ruy Lopez.}
4. Ba4 Nf6 5. O-O Be7 6. Re1 b5 7. Bb3 d6 8. c3 O-O 9. h3 Nb8 10. d4 Nbd7
11. c4 c6 12. cxb5 axb5 13. Nc3 Bb7 14. Bg5 b4 15. Nb1 h6 16. Bh4 c5 17. dxe5
Nxe4 18. Bxe7 Qxe7 19. exd6 Qf6 20. Nbd2 Nxd6 21. Nc4 Nxc4 22. Bxc4 Nb6
23. Ne5 Rae8 24. Bxf7+ Rxf7 25. Nxf7 Rxe1+ 26. Qxe1 Kxf7 27. Qe3 Qg5 28. Qxg5
hxg5 29. b3 Ke6 30. a3 Kd6 31. axb4 cxb4 32. Ra5 Nd5 33. f3 Bc8 34. Kf2 Bf5
35. Ra7 g6 36. Ra6+ Kc5 37. Ke1 Nf4 38. g3 Nxh3 39. Kd2 Kb5 40. Rd6 Kc5 41. Ra6
Nf2 42. g4 Bd3 43. Re6 1/2-1/2
```
#### Read PGN
PGN supplied as an optional parameter to the NewGame constructor:
```go
pgn, err := chess.PGN(pgnReader)
if err != nil {
// handle error
}
game := chess.NewGame(pgn)
```
#### Write PGN
Moves and tag pairs added to the PGN output:
```go
game := chess.NewGame()
game.AddTagPair("Event", "F/S Return Match")
game.MoveStr("e4")
game.MoveStr("e5")
fmt.Println(game)
/*
[Event "F/S Return Match"]
1.e4 e5 *
*/
```
### FEN
[FEN](https://en.wikipedia.org/wiki/Forsyth–Edwards_Notation), or Forsyth–Edwards Notation, is the standard notation for describing a board position. FENs include piece positions, turn, castle rights, en passant square, half move counter (for [50 move rule](https://en.wikipedia.org/wiki/Fifty-move_rule)), and full move counter.
#### Read FEN
FEN supplied as an optional parameter to the NewGame constructor:
```go
fen, err := chess.FEN("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1")
if err != nil {
// handle error
}
game := chess.NewGame(fen)
```
#### Write FEN
Game's current position outputted in FEN notation:
```go
game := chess.NewGame()
pos := game.Position()
fmt.Println(pos.String()) // rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1
```
### Notations
[Chess Notation](https://en.wikipedia.org/wiki/Chess_notation) define how moves are encoded in a serialized format. Chess uses a notation when converting to and from PGN and for accepting move text.
#### Algebraic Notation
[Algebraic Notation](https://en.wikipedia.org/wiki/Algebraic_notation_(chess)) (or Standard Algebraic Notation) is the official chess notation used by FIDE. Examples: e2, e5, O-O (short castling), e8=Q (promotion)
```go
game := chess.NewGame(chess.UseNotation(chess.AlgebraicNotation{}))
game.MoveStr("e4")
game.MoveStr("e5")
fmt.Println(game) // 1.e4 e5 *
```
#### Long AlgebraicNotation Notation
LongAlgebraicNotation is a more computer friendly alternative to algebraic notation. This notation uses the same format as the UCI (Universal Chess Interface). Examples: e2e4, e7e5, e1g1 (white short castling), e7e8q (for promotion)
```go
game := chess.NewGame(chess.UseNotation(chess.LongAlgebraicNotation{}))
game.MoveStr("e2e4")
game.MoveStr("e7e5")
fmt.Println(game) // 1.e2e4 e7e5 *
```
### Visualization
Chess has multiple ways to visualize board positions for debugging and presentation.
#### Text Representation
Board's Draw() method can be used to visualize a position using unicode chess symbols.
```go
game := chess.NewGame()
fmt.Println(game.Position().Board().Draw())
/*
A B C D E F G H
8♜ ♞ ♝ ♛ ♚ ♝ ♞ ♜
7♟ ♟ ♟ ♟ ♟ ♟ ♟ ♟
6- - - - - - - -
5- - - - - - - -
4- - - - - - - -
3- - - - - - - -
2♙ ♙ ♙ ♙ ♙ ♙ ♙ ♙
1♖ ♘ ♗ ♕ ♔ ♗ ♘ ♖
*/
```
#### SVG Representation
[chess/image](https://github.com/notnil/chess/image) is an image utility designed to work with the chess package to render an SVG of a chess position. It presents configuration options such as dark and white square colors and squares to highlight.
```go
// create file
f, err := os.Create("example.svg")
if err != nil {
// handle error
}
defer f.Close()
// create board position
fenStr := "rnbqkbnr/pppppppp/8/8/3P4/8/PPP1PPPP/RNBQKBNR b KQkq - 0 1"
pos := &chess.Position{}
if err := pos.UnmarshalText([]byte(fenStr)); err != nil {
// handle error
}
// write board SVG to file
yellow := color.RGBA{255, 255, 0, 1}
mark := image.MarkSquares(yellow, chess.D2, chess.D4)
if err := image.SVG(f, pos.Board(), mark); err != nil {
// handle error
}
```
![rnbqkbnr/pppppppp/8/8/3P4/8/PPP1PPPP/RNBQKBNR b KQkq - 0 1](example.png)
### Example Program
Valid moves are randomly selected until the game is over:
```go
package main
import (
"fmt"
"math/rand"
"github.com/notnil/chess"
)
func main() {
game := chess.NewGame()
// generate moves until game is over
for game.Outcome() == chess.NoOutcome {
// select a random move
moves := game.ValidMoves()
move := moves[rand.Intn(len(moves))]
game.Move(move)
}
// print outcome and game PGN
fmt.Println(game.Position().Board().Draw())
fmt.Printf("Game completed. %s by %s.\n", game.Outcome(), game.Method())
fmt.Println(game.String())
}
```
## Performance
Chess has been performance tuned, using [pprof](https://golang.org/pkg/runtime/pprof/), with the goal of being fast enough for use by chess bots. The original map based board representation was replaced by [bitboards](https://chessprogramming.wikispaces.com/Bitboards) resulting in a large performance increase.
### Benchmarks
The benchmarks can be run with the following command:
```
go test -bench=.
```
Results from the baseline 2015 MBP:
```
BenchmarkBitboardReverse-4 2000000000 1.01 ns/op
BenchmarkStalemateStatus-4 500000 3116 ns/op
BenchmarkInvalidStalemateStatus-4 500000 2290 ns/op
BenchmarkPositionHash-4 1000000 1864 ns/op
BenchmarkValidMoves-4 100000 13445 ns/op
BenchmarkPGN-4 300 5549192 ns/op
```

135
assets/valid_notation_tests.json

@ -0,0 +1,135 @@ @@ -0,0 +1,135 @@
[
{
"pos1" : "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1",
"pos2" : "rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR b KQkq e3 0 1",
"algText" : "e4",
"longAlgText" : "e2e4",
"description" : "opening move"
},
{
"pos1" : "rn1qkb1r/pp3ppp/2p1pn2/3p4/2PP4/2NQPN2/PP3PPP/R1B1K2R b KQkq - 0 7",
"pos2" : "r2qkb1r/pp1n1ppp/2p1pn2/3p4/2PP4/2NQPN2/PP3PPP/R1B1K2R w KQkq - 1 8",
"algText" : "Nbd7",
"longAlgText" : "b8d7",
"description" : "http://en.lichess.org/W91M4jms#14"
},
{
"pos1" : "r2qk2r/pp1n1ppp/2pbpn2/3p4/2PP4/1PNQPN2/P4PPP/R1B1K2R w KQkq - 1 9",
"pos2" : "r2qk2r/pp1n1ppp/2pbpn2/3p4/2PP4/1PNQPN2/P4PPP/R1B2RK1 b kq - 0 9",
"algText" : "O-O",
"longAlgText" : "e1g1",
"description" : "http://en.lichess.org/W91M4jms#17"
},
{
"pos1" : "3r1rk1/pp1nqppp/2pbpn2/3p4/2PP4/1PNQPN2/PB3PPP/3RR1K1 b - - 5 12",
"pos2" : "3r1rk1/pp1nqppp/2pbpn2/8/2pP4/1PNQPN2/PB3PPP/3RR1K1 w - - 0 13",
"algText" : "dxc4",
"longAlgText" : "d5c4",
"description" : "http://en.lichess.org/W91M4jms#24"
},
{
"pos1" : "3r2k1/pp2qppp/2p2n2/4b3/2P5/2N1P3/PB2QPPP/3R2K1 w - - 1 18",
"pos2" : "3R2k1/pp2qppp/2p2n2/4b3/2P5/2N1P3/PB2QPPP/6K1 b - - 0 18",
"algText" : "Rxd8+",
"longAlgText" : "d1d8",
"description" : "http://en.lichess.org/W91M4jms#35"
},
{
"pos1" : "rnbqkbnr/pppppppp/8/8/3P4/8/PPP1PPPP/RNBQKBNR b KQkq d3 0 1",
"pos2" : "rnbqkb1r/pppppppp/5n2/8/3P4/8/PPP1PPPP/RNBQKBNR w KQkq - 1 2",
"algText" : "Nf6",
"longAlgText" : "g8f6",
"description" : "https://en.lichess.org/W91M4jms#2"
},
{
"pos1" : "r4rk1/ppbn1p2/2p1pn1p/q2p2pb/7N/PP1PP1PP/1BPN1PB1/R3QRK1 w - g6 0 14",
"pos2" : "r4rk1/ppbn1p2/2p1pn1p/q2p2pb/8/PP1PPNPP/1BPN1PB1/R3QRK1 b - - 1 14",
"algText" : "Nhf3",
"longAlgText" : "h4f3",
"description" : "http://en.lichess.org/4HXJOtpN#27"
},
{
"pos1" : "4r3/8/2p2PPk/1p1r4/pP2p1R1/P1B5/2P2K2/8 b - - 0 44",
"pos2" : "4r3/8/2p2PPk/1p6/pP2p1R1/P1B5/2P2K2/3r4 w - - 1 45",
"algText" : "Rd1??",
"longAlgText" : "d5d1",
"description" : "http://en.lichess.org/4HXJOtpN#87"
},
{
"pos1" : "8/3k2Kp/p7/1p1r4/6P1/8/Pn1p4/7R b - - 0 40",
"pos2" : "8/3k2Kp/p7/1p1r4/6P1/8/Pn6/3q3R w - - 0 41",
"algText" : "d1=Q",
"longAlgText" : "d2d1q",
"description" : "http://en.lichess.org/YXPuk8kg#80"
},
{
"pos1" : "rnbk1b1r/p3pppp/5n2/2p1p3/5B2/2N2P2/PPP3PP/R3KBNR w KQ - 0 10",
"pos2" : "rnbk1b1r/p3pppp/5n2/2p1p3/5B2/2N2P2/PPP3PP/2KR1BNR b - - 0 10",
"algText" : "O-O-O+",
"longAlgText" : "e1c1",
"description" : "http://en.lichess.org/dimuEVR0#19"
},
{
"pos1" : "4R3/1r1k2pp/p1p5/1pP5/8/8/1PP3PP/2K1Rr2 w - - 5 32",
"pos2" : "8/1r1kR1pp/p1p5/1pP5/8/8/1PP3PP/2K1Rr2 b - - 6 32",
"algText" : "Re7+",
"longAlgText" : "e8e7",
"description" : "only 1 rook can move because of pin http://en.lichess.org/JCRBhXH7#62"
},
{
"pos1" : "rn1qkb1r/pp3ppp/2p1pn2/3p4/2PP4/2NbPN2/PP3PPP/R1BQK2R w KQkq - 0 7",
"pos2" : "rn1qkb1r/pp3ppp/2p1pn2/3p4/2PP4/2NQPN2/PP3PPP/R1B1K2R b KQkq - 0 7",
"algText" : "Qxd3",
"longAlgText" : "d1d3",
"description" : "https://en.lichess.org/editor/rn1qkb1r/pp3ppp/2p1pn2/3p4/2PP4/2NbPN2/PP3PPP/R1BQK2R_w_KQkq_-_0_7"
},
{
"pos1" : "7k/pBp2Rb1/4Q2p/4p3/1P4p1/2P4q/P2r1P2/6K1 b - - 1 38",
"pos2" : "7k/pBp2Rb1/4Q2p/4p3/1P4p1/2P4q/P4P2/3r2K1 w - - 2 39",
"algText" : "Rd1#",
"longAlgText" : "d2d1",
"description" : "https://en.lichess.org/editor/7k/pBp2Rb1/4Q2p/4p3/1P4p1/2P4q/P2r1P2/6K1_b_-_-_1_38"
},
{
"pos1" : "r1b2rk1/pp2b1pp/1qn1p3/3pPp2/1P1P4/P2BPN2/6PP/RN1Q1RK1 w - f6 0 13",
"pos2" : "r1b2rk1/pp2b1pp/1qn1pP2/3p4/1P1P4/P2BPN2/6PP/RN1Q1RK1 b - - 0 13",
"algText" : "exf6",
"longAlgText" : "e5f6",
"description" : "https://en.lichess.org/editor/r1b2rk1/pp2b1pp/1qn1p3/3pPp2/1P1P4/P2BPN2/6PP/RN1Q1RK1_w_-_f6_0_13"
},
{
"pos1" : "3r2k1/b4p2/p1p4p/6p1/4pNb1/1P2P2P/PB3PP1/R5K1 w - g6 0 27",
"pos2" : "3r2k1/b4p2/p1p3Np/6p1/4p1b1/1P2P2P/PB3PP1/R5K1 b - - 1 27",
"algText" : "Ng6",
"longAlgText" : "f4g6",
"description" : "https://en.lichess.org/editor/3r2k1/b4p2/p1p4p/6p1/4pNb1/1P2P2P/PB3PP1/R5K1_w_-_g6_0_27"
},
{
"pos1" : "r1b1k2r/ppp2ppp/2p2n2/4N3/4P3/2P5/PPP2PPP/R1BK3R b kq - 0 8",
"pos2" : "r1b1k2r/ppp2ppp/2p5/4N3/4n3/2P5/PPP2PPP/R1BK3R w kq - 0 9",
"algText" : "Nxe4",
"longAlgText" : "f6e4",
"description" : "https://en.lichess.org/editor/r1b1k2r/ppp2ppp/2p2n2/4N3/4P3/2P5/PPP2PPP/R1BK3R_b_kq_-_0_8"
},
{
"pos1" : "r1bqk2r/ppp2ppp/2p2n2/4N3/4P3/2P5/PPP2PPP/R1BQK2R b KQkq - 0 7",
"pos2" : "r1b1k2r/ppp2ppp/2p2n2/4N3/4P3/2P5/PPP2PPP/R1BqK2R w KQkq - 0 8",
"algText" : "Qxd1",
"longAlgText" : "d8d1",
"description" : "https://en.lichess.org/editor/r1bqk2r/ppp2ppp/2p2n2/4N3/4P3/2P5/PPP2PPP/R1BQK2R_b_KQkq_-_0_7"
},
{
"pos1" : "2k5/6K1/8/8/8/8/4qq2/4q3 b - - 9 55",
"pos2" : "2k5/6K1/8/8/8/8/4qq2/5q2 w - - 10 56",
"algText" : "Q1f1",
"longAlgText" : "e1f1",
"description" : "https://lichess.org/editor/2k5/6K1/8/8/8/8/4qq2/4q3_b_-_-_9_55"
},
{
"pos1" : "8/2B4R/1N6/2Q5/1N1N4/2N5/5k2/1K5R w - - 17 72",
"pos2" : "8/2B4R/8/2QN4/1N1N4/2N5/5k2/1K5R b - - 18 72",
"algText" : "N6d5",
"longAlgText" : "b6d5",
"description" : "https://lichess.org/editor/8/2B4R/1N6/2Q5/1N1N4/2N5/5k2/1K5R_w_-_-_17_72"
}
]

61
bitboard.go

@ -0,0 +1,61 @@ @@ -0,0 +1,61 @@
package chess
import (
"strconv"
"strings"
)
// bitboard is a board representation encoded in an unsigned 64-bit integer. The
// 64 board positions begin with A1 as the most significant bit and H8 as the least.
type bitboard uint64
func newBitboard(m map[Square]bool) bitboard {
s := ""
for sq := 0; sq < numOfSquaresInBoard; sq++ {
if m[Square(sq)] {
s += "1"
} else {
s += "0"
}
}
bb, err := strconv.ParseUint(s, 2, 64)
if err != nil {
panic(err)
}
return bitboard(bb)
}
func (b bitboard) Mapping() map[Square]bool {
m := map[Square]bool{}
for sq := 0; sq < numOfSquaresInBoard; sq++ {
if b&bbForSquare(Square(sq)) > 0 {
m[Square(sq)] = true
}
}
return m
}
// String returns a 64 character string of 1s and 0s starting with the most significant bit.
func (b bitboard) String() string {
s := strconv.FormatUint(uint64(b), 2)
return strings.Repeat("0", numOfSquaresInBoard-len(s)) + s
}
// Draw returns visual representation of the bitboard useful for debugging.
func (b bitboard) Draw() string {
s := "\n A B C D E F G H\n"
for r := 7; r >= 0; r-- {
s += Rank(r).String()
for f := 0; f < numOfSquaresInRow; f++ {
sq := getSquare(File(f), Rank(r))
if b.Occupied(sq) {
s += "1"
} else {
s += "0"
}
s += " "
}
s += "\n"
}
return s
}

15
bitboard_postbits.go

@ -0,0 +1,15 @@ @@ -0,0 +1,15 @@
// +build go1.9
package chess
import "math/bits"
// Reverse returns a bitboard where the bit order is reversed.
func (b bitboard) Reverse() bitboard {
return bitboard(bits.Reverse64(uint64(b)))
}
// Occupied returns true if the square's bitboard position is 1.
func (b bitboard) Occupied(sq Square) bool {
return (bits.RotateLeft64(uint64(b), int(sq)+1) & 1) == 1
}

42
bitboard_prebits.go

@ -0,0 +1,42 @@ @@ -0,0 +1,42 @@
// +build !go1.9
package chess
// Reverse returns a bitboard where the bit order is reversed.
// Implementation from: http://stackoverflow.com/questions/746171/best-algorithm-for-bit-reversal-from-msb-lsb-to-lsb-msb-in-c
func (b bitboard) Reverse() bitboard {
return bitboard((bitReverseLookupTable[b&0xff] << 56) |
(bitReverseLookupTable[(b>>8)&0xff] << 48) |
(bitReverseLookupTable[(b>>16)&0xff] << 40) |
(bitReverseLookupTable[(b>>24)&0xff] << 32) |
(bitReverseLookupTable[(b>>32)&0xff] << 24) |
(bitReverseLookupTable[(b>>40)&0xff] << 16) |
(bitReverseLookupTable[(b>>48)&0xff] << 8) |
(bitReverseLookupTable[(b>>56)&0xff]))
}
var bitReverseLookupTable = []uint64{
0x00, 0x80, 0x40, 0xC0, 0x20, 0xA0, 0x60, 0xE0, 0x10, 0x90, 0x50, 0xD0, 0x30, 0xB0, 0x70, 0xF0,
0x08, 0x88, 0x48, 0xC8, 0x28, 0xA8, 0x68, 0xE8, 0x18, 0x98, 0x58, 0xD8, 0x38, 0xB8, 0x78, 0xF8,
0x04, 0x84, 0x44, 0xC4, 0x24, 0xA4, 0x64, 0xE4, 0x14, 0x94, 0x54, 0xD4, 0x34, 0xB4, 0x74, 0xF4,
0x0C, 0x8C, 0x4C, 0xCC, 0x2C, 0xAC, 0x6C, 0xEC, 0x1C, 0x9C, 0x5C, 0xDC, 0x3C, 0xBC, 0x7C, 0xFC,
0x02, 0x82, 0x42, 0xC2, 0x22, 0xA2, 0x62, 0xE2, 0x12, 0x92, 0x52, 0xD2, 0x32, 0xB2, 0x72, 0xF2,
0x0A, 0x8A, 0x4A, 0xCA, 0x2A, 0xAA, 0x6A, 0xEA, 0x1A, 0x9A, 0x5A, 0xDA, 0x3A, 0xBA, 0x7A, 0xFA,
0x06, 0x86, 0x46, 0xC6, 0x26, 0xA6, 0x66, 0xE6, 0x16, 0x96, 0x56, 0xD6, 0x36, 0xB6, 0x76, 0xF6,
0x0E, 0x8E, 0x4E, 0xCE, 0x2E, 0xAE, 0x6E, 0xEE, 0x1E, 0x9E, 0x5E, 0xDE, 0x3E, 0xBE, 0x7E, 0xFE,
0x01, 0x81, 0x41, 0xC1, 0x21, 0xA1, 0x61, 0xE1, 0x11, 0x91, 0x51, 0xD1, 0x31, 0xB1, 0x71, 0xF1,
0x09, 0x89, 0x49, 0xC9, 0x29, 0xA9, 0x69, 0xE9, 0x19, 0x99, 0x59, 0xD9, 0x39, 0xB9, 0x79, 0xF9,
0x05, 0x85, 0x45, 0xC5, 0x25, 0xA5, 0x65, 0xE5, 0x15, 0x95, 0x55, 0xD5, 0x35, 0xB5, 0x75, 0xF5,
0x0D, 0x8D, 0x4D, 0xCD, 0x2D, 0xAD, 0x6D, 0xED, 0x1D, 0x9D, 0x5D, 0xDD, 0x3D, 0xBD, 0x7D, 0xFD,
0x03, 0x83, 0x43, 0xC3, 0x23, 0xA3, 0x63, 0xE3, 0x13, 0x93, 0x53, 0xD3, 0x33, 0xB3, 0x73, 0xF3,
0x0B, 0x8B, 0x4B, 0xCB, 0x2B, 0xAB, 0x6B, 0xEB, 0x1B, 0x9B, 0x5B, 0xDB, 0x3B, 0xBB, 0x7B, 0xFB,
0x07, 0x87, 0x47, 0xC7, 0x27, 0xA7, 0x67, 0xE7, 0x17, 0x97, 0x57, 0xD7, 0x37, 0xB7, 0x77, 0xF7,
0x0F, 0x8F, 0x4F, 0xCF, 0x2F, 0xAF, 0x6F, 0xEF, 0x1F, 0x9F, 0x5F, 0xDF, 0x3F, 0xBF, 0x7F, 0xFF,
}
// Occupied returns true if the square's bitboard position is 1.
func (b bitboard) Occupied(sq Square) bool {
return (uint64(b) >> uint64(63-sq) & 1) == 1
}
//

55
bitboard_test.go

@ -0,0 +1,55 @@ @@ -0,0 +1,55 @@
package chess
import "testing"
type bitboardTestPair struct {
initial uint64
reversed uint64
}
var (
tests = []bitboardTestPair{
bitboardTestPair{
uint64(1),
uint64(9223372036854775808),
},
bitboardTestPair{
uint64(18446744073709551615),
uint64(18446744073709551615),
},
bitboardTestPair{
uint64(0),
uint64(0),
},
}
)
func TestBitboardReverse(t *testing.T) {
for _, p := range tests {
r := uint64(bitboard(p.initial).Reverse())
if r != p.reversed {
t.Fatalf("bitboard reverse of %s expected %s but got %s", intStr(p.initial), intStr(p.reversed), intStr(r))
}
}
}
func TestBitboardOccupied(t *testing.T) {
m := map[Square]bool{
B3: true,
}
bb := newBitboard(m)
if bb.Occupied(B3) != true {
t.Fatalf("bitboard occupied of %s expected %t but got %t", bb, true, false)
}
}
func BenchmarkBitboardReverse(b *testing.B) {
for i := 0; i < b.N; i++ {
u := uint64(9223372036854775807)
bitboard(u).Reverse()
}
}
func intStr(i uint64) string {
return bitboard(i).String()
}

424
board.go

@ -0,0 +1,424 @@ @@ -0,0 +1,424 @@
package chess
import (
"bytes"
"encoding/binary"
"errors"
"strconv"
"strings"
)
// A Board represents a chess board and its relationship between squares and pieces.
type Board struct {
bbWhiteKing bitboard
bbWhiteQueen bitboard
bbWhiteRook bitboard
bbWhiteBishop bitboard
bbWhiteKnight bitboard
bbWhitePawn bitboard
bbBlackKing bitboard
bbBlackQueen bitboard
bbBlackRook bitboard
bbBlackBishop bitboard
bbBlackKnight bitboard
bbBlackPawn bitboard
whiteSqs bitboard
blackSqs bitboard
emptySqs bitboard
whiteKingSq Square
blackKingSq Square
}
// NewBoard returns a board from a square to piece mapping.
func NewBoard(m map[Square]Piece) *Board {
b := &Board{}
for _, p1 := range allPieces {
bm := map[Square]bool{}
for sq, p2 := range m {
if p1 == p2 {
bm[sq] = true
}
}
bb := newBitboard(bm)
b.setBBForPiece(p1, bb)
}
b.calcConvienceBBs(nil)
return b
}
// SquareMap returns a mapping of squares to pieces. A square is only added to the map if it is occupied.
func (b *Board) SquareMap() map[Square]Piece {
m := map[Square]Piece{}
for sq := 0; sq < numOfSquaresInBoard; sq++ {
p := b.Piece(Square(sq))
if p != NoPiece {
m[Square(sq)] = p
}
}
return m
}
// Rotate rotates the board 90 degrees clockwise.
func (b *Board) Rotate() *Board {
return b.Flip(UpDown).Transpose()
}
// FlipDirection is the direction for the Board.Flip method
type FlipDirection int
const (
// UpDown flips the board's rank values
UpDown FlipDirection = iota
// LeftRight flips the board's file values
LeftRight
)
// Flip flips the board over the vertical or hoizontal
// center line.
func (b *Board) Flip(fd FlipDirection) *Board {
m := map[Square]Piece{}
for sq := 0; sq < numOfSquaresInBoard; sq++ {
var mv Square
switch fd {
case UpDown:
file := Square(sq).File()
rank := Rank(7 - Square(sq).Rank())
mv = getSquare(file, rank)
case LeftRight:
file := File(7 - Square(sq).File())
rank := Square(sq).Rank()
mv = getSquare(file, rank)
}
m[mv] = b.Piece(Square(sq))
}
return NewBoard(m)
}
// Transpose flips the board over the A8 to H1 diagonal.
func (b *Board) Transpose() *Board {
m := map[Square]Piece{}
for sq := 0; sq < numOfSquaresInBoard; sq++ {
file := File(7 - Square(sq).Rank())
rank := Rank(7 - Square(sq).File())
mv := getSquare(file, rank)
m[mv] = b.Piece(Square(sq))
}
return NewBoard(m)
}
// Draw returns visual representation of the board useful for debugging.
func (b *Board) Draw() string {
s := "\n A B C D E F G H\n"
for r := 7; r >= 0; r-- {
s += Rank(r).String()
for f := 0; f < numOfSquaresInRow; f++ {
p := b.Piece(getSquare(File(f), Rank(r)))
if p == NoPiece {
s += "-"
} else {
s += p.String()
}
s += " "
}
s += "\n"
}
return s
}
// String implements the fmt.Stringer interface and returns
// a string in the FEN board format: rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR
func (b *Board) String() string {
fen := ""
for r := 7; r >= 0; r-- {
for f := 0; f < numOfSquaresInRow; f++ {
sq := getSquare(File(f), Rank(r))
p := b.Piece(sq)
if p != NoPiece {
fen += p.getFENChar()
} else {
fen += "1"
}
}
if r != 0 {
fen += "/"
}
}
for i := 8; i > 1; i-- {
repeatStr := strings.Repeat("1", i)
countStr := strconv.Itoa(i)
fen = strings.Replace(fen, repeatStr, countStr, -1)
}
return fen
}
// Piece returns the piece for the given square.
func (b *Board) Piece(sq Square) Piece {
for _, p := range allPieces {
bb := b.bbForPiece(p)
if bb.Occupied(sq) {
return p
}
}
return NoPiece
}
// MarshalText implements the encoding.TextMarshaler interface and returns
// a string in the FEN board format: rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR
func (b *Board) MarshalText() (text []byte, err error) {
return []byte(b.String()), nil
}
// UnmarshalText implements the encoding.TextUnarshaler interface and takes
// a string in the FEN board format: rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR
func (b *Board) UnmarshalText(text []byte) error {
cp, err := fenBoard(string(text))
if err != nil {
return err
}
*b = *cp
return nil
}
// MarshalBinary implements the encoding.BinaryMarshaler interface and returns
// the bitboard representations as a array of bytes. Bitboads are encoded
// in the following order: WhiteKing, WhiteQueen, WhiteRook, WhiteBishop, WhiteKnight
// WhitePawn, BlackKing, BlackQueen, BlackRook, BlackBishop, BlackKnight, BlackPawn
func (b *Board) MarshalBinary() (data []byte, err error) {
bbs := []bitboard{b.bbWhiteKing, b.bbWhiteQueen, b.bbWhiteRook, b.bbWhiteBishop, b.bbWhiteKnight, b.bbWhitePawn,
b.bbBlackKing, b.bbBlackQueen, b.bbBlackRook, b.bbBlackBishop, b.bbBlackKnight, b.bbBlackPawn}
buf := new(bytes.Buffer)
err = binary.Write(buf, binary.BigEndian, bbs)
return buf.Bytes(), err
}
// UnmarshalBinary implements the encoding.BinaryUnmarshaler interface and parses
// the bitboard representations as a array of bytes. Bitboads are decoded
// in the following order: WhiteKing, WhiteQueen, WhiteRook, WhiteBishop, WhiteKnight
// WhitePawn, BlackKing, BlackQueen, BlackRook, BlackBishop, BlackKnight, BlackPawn
func (b *Board) UnmarshalBinary(data []byte) error {
if len(data) != 96 {
return errors.New("chess: invalid number of bytes for board unmarshal binary")
}
b.bbWhiteKing = bitboard(binary.BigEndian.Uint64(data[:8]))
b.bbWhiteQueen = bitboard(binary.BigEndian.Uint64(data[8:16]))
b.bbWhiteRook = bitboard(binary.BigEndian.Uint64(data[16:24]))
b.bbWhiteBishop = bitboard(binary.BigEndian.Uint64(data[24:32]))
b.bbWhiteKnight = bitboard(binary.BigEndian.Uint64(data[32:40]))
b.bbWhitePawn = bitboard(binary.BigEndian.Uint64(data[40:48]))
b.bbBlackKing = bitboard(binary.BigEndian.Uint64(data[48:56]))
b.bbBlackQueen = bitboard(binary.BigEndian.Uint64(data[56:64]))
b.bbBlackRook = bitboard(binary.BigEndian.Uint64(data[64:72]))
b.bbBlackBishop = bitboard(binary.BigEndian.Uint64(data[72:80]))
b.bbBlackKnight = bitboard(binary.BigEndian.Uint64(data[80:88]))
b.bbBlackPawn = bitboard(binary.BigEndian.Uint64(data[88:96]))
b.calcConvienceBBs(nil)
return nil
}
func (b *Board) update(m *Move) {
p1 := b.Piece(m.s1)
s1BB := bbForSquare(m.s1)
s2BB := bbForSquare(m.s2)
// move s1 piece to s2
for _, p := range allPieces {
bb := b.bbForPiece(p)
// remove what was at s2
b.setBBForPiece(p, bb & ^s2BB)
// move what was at s1 to s2
if bb.Occupied(m.s1) {
bb = b.bbForPiece(p)
b.setBBForPiece(p, (bb & ^s1BB)|s2BB)
}
}
// check promotion
if m.promo != NoPieceType {
newPiece := getPiece(m.promo, p1.Color())
// remove pawn
bbPawn := b.bbForPiece(p1)
b.setBBForPiece(p1, bbPawn & ^s2BB)
// add promo piece
bbPromo := b.bbForPiece(newPiece)
b.setBBForPiece(newPiece, bbPromo|s2BB)
}
// remove captured en passant piece
if m.HasTag(EnPassant) {
if p1.Color() == White {
b.bbBlackPawn = ^(bbForSquare(m.s2) << 8) & b.bbBlackPawn
} else {
b.bbWhitePawn = ^(bbForSquare(m.s2) >> 8) & b.bbWhitePawn
}
}
// move rook for castle
if p1.Color() == White && m.HasTag(KingSideCastle) {
b.bbWhiteRook = (b.bbWhiteRook & ^bbForSquare(H1) | bbForSquare(F1))
} else if p1.Color() == White && m.HasTag(QueenSideCastle) {
b.bbWhiteRook = (b.bbWhiteRook & ^bbForSquare(A1)) | bbForSquare(D1)
} else if p1.Color() == Black && m.HasTag(KingSideCastle) {
b.bbBlackRook = (b.bbBlackRook & ^bbForSquare(H8) | bbForSquare(F8))
} else if p1.Color() == Black && m.HasTag(QueenSideCastle) {
b.bbBlackRook = (b.bbBlackRook & ^bbForSquare(A8)) | bbForSquare(D8)
}
b.calcConvienceBBs(m)
}
func (b *Board) calcConvienceBBs(m *Move) {
whiteSqs := b.bbWhiteKing | b.bbWhiteQueen | b.bbWhiteRook | b.bbWhiteBishop | b.bbWhiteKnight | b.bbWhitePawn
blackSqs := b.bbBlackKing | b.bbBlackQueen | b.bbBlackRook | b.bbBlackBishop | b.bbBlackKnight | b.bbBlackPawn
emptySqs := ^(whiteSqs | blackSqs)
b.whiteSqs = whiteSqs
b.blackSqs = blackSqs
b.emptySqs = emptySqs
if m == nil {
b.whiteKingSq = NoSquare
b.blackKingSq = NoSquare
for sq := 0; sq < numOfSquaresInBoard; sq++ {
sqr := Square(sq)
if b.bbWhiteKing.Occupied(sqr) {
b.whiteKingSq = sqr
} else if b.bbBlackKing.Occupied(sqr) {
b.blackKingSq = sqr
}
}
} else if m.s1 == b.whiteKingSq {
b.whiteKingSq = m.s2
} else if m.s1 == b.blackKingSq {
b.blackKingSq = m.s2
}
}
func (b *Board) copy() *Board {
return &Board{
whiteSqs: b.whiteSqs,
blackSqs: b.blackSqs,
emptySqs: b.emptySqs,
whiteKingSq: b.whiteKingSq,
blackKingSq: b.blackKingSq,
bbWhiteKing: b.bbWhiteKing,
bbWhiteQueen: b.bbWhiteQueen,
bbWhiteRook: b.bbWhiteRook,
bbWhiteBishop: b.bbWhiteBishop,
bbWhiteKnight: b.bbWhiteKnight,
bbWhitePawn: b.bbWhitePawn,
bbBlackKing: b.bbBlackKing,
bbBlackQueen: b.bbBlackQueen,
bbBlackRook: b.bbBlackRook,
bbBlackBishop: b.bbBlackBishop,
bbBlackKnight: b.bbBlackKnight,
bbBlackPawn: b.bbBlackPawn,
}
}
func (b *Board) isOccupied(sq Square) bool {
return !b.emptySqs.Occupied(sq)
}
func (b *Board) hasSufficientMaterial() bool {
// queen, rook, or pawn exist
if (b.bbWhiteQueen | b.bbWhiteRook | b.bbWhitePawn |
b.bbBlackQueen | b.bbBlackRook | b.bbBlackPawn) > 0 {
return true
}
// if king is missing then it is a test
if b.bbWhiteKing == 0 || b.bbBlackKing == 0 {
return true
}
count := map[PieceType]int{}
pieceMap := b.SquareMap()
for _, p := range pieceMap {
count[p.Type()]++
}
// king versus king
if count[Bishop] == 0 && count[Knight] == 0 {
return false
}
// king and bishop versus king
if count[Bishop] == 1 && count[Knight] == 0 {
return false
}
// king and knight versus king
if count[Bishop] == 0 && count[Knight] == 1 {
return false
}
// king and bishop(s) versus king and bishop(s) with the bishops on the same colour.
if count[Knight] == 0 {
whiteCount := 0
blackCount := 0
for sq, p := range pieceMap {
if p.Type() == Bishop {
switch sq.color() {
case White:
whiteCount++
case Black:
blackCount++
}
}
}
if whiteCount == 0 || blackCount == 0 {
return false
}
}
return true
}
func (b *Board) bbForPiece(p Piece) bitboard {
switch p {
case WhiteKing:
return b.bbWhiteKing
case WhiteQueen:
return b.bbWhiteQueen
case WhiteRook:
return b.bbWhiteRook
case WhiteBishop:
return b.bbWhiteBishop
case WhiteKnight:
return b.bbWhiteKnight
case WhitePawn:
return b.bbWhitePawn
case BlackKing:
return b.bbBlackKing
case BlackQueen:
return b.bbBlackQueen
case BlackRook:
return b.bbBlackRook
case BlackBishop:
return b.bbBlackBishop
case BlackKnight:
return b.bbBlackKnight
case BlackPawn:
return b.bbBlackPawn
}
return bitboard(0)
}
func (b *Board) setBBForPiece(p Piece, bb bitboard) {
switch p {
case WhiteKing:
b.bbWhiteKing = bb
case WhiteQueen:
b.bbWhiteQueen = bb
case WhiteRook:
b.bbWhiteRook = bb
case WhiteBishop:
b.bbWhiteBishop = bb
case WhiteKnight:
b.bbWhiteKnight = bb
case WhitePawn:
b.bbWhitePawn = bb
case BlackKing:
b.bbBlackKing = bb
case BlackQueen:
b.bbBlackQueen = bb
case BlackRook:
b.bbBlackRook = bb
case BlackBishop:
b.bbBlackBishop = bb
case BlackKnight:
b.bbBlackKnight = bb
case BlackPawn:
b.bbBlackPawn = bb
default:
panic("invalid piece")
}
}

90
board_test.go

@ -0,0 +1,90 @@ @@ -0,0 +1,90 @@
package chess
import (
"testing"
)
func TestBoardTextSerialization(t *testing.T) {
fen := "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR"
b := &Board{}
if err := b.UnmarshalText([]byte(fen)); err != nil {
t.Fatal("recieved unexpected error", err)
}
txt, err := b.MarshalText()
if err != nil {
t.Fatal("recieved unexpected error", err)
}
if fen != string(txt) {
t.Fatalf("fen expected board string %s but got %s", fen, string(txt))
}
}
func TestBoardBinarySerialization(t *testing.T) {
g := NewGame()
board := g.Position().Board()
b, err := board.MarshalBinary()
if err != nil {
t.Fatal("recieved unexpected error", err)
}
cpBoard := &Board{}
err = cpBoard.UnmarshalBinary(b)
if err != nil {
t.Fatal("recieved unexpected error", err)
}
s := "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR"
if s != cpBoard.String() {
t.Fatalf("expected board string %s but got %s", s, cpBoard.String())
}
}
func TestBoardRotation(t *testing.T) {
fens := []string{
"RP4pr/NP4pn/BP4pb/QP4pq/KP4pk/BP4pb/NP4pn/RP4pr",
"RNBKQBNR/PPPPPPPP/8/8/8/8/pppppppp/rnbkqbnr",
"rp4PR/np4PN/bp4PB/kp4PK/qp4PQ/bp4PB/np4PN/rp4PR",
"rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR",
}
g := NewGame()
board := g.Position().Board()
for i := 0; i < 4; i++ {
board = board.Rotate()
if fens[i] != board.String() {
t.Fatalf("expected board string %s but got %s", fens[i], board.String())
}
}
}
func TestBoardFlip(t *testing.T) {
g := NewGame()
board := g.Position().Board()
board = board.Flip(UpDown)
b := "RNBQKBNR/PPPPPPPP/8/8/8/8/pppppppp/rnbqkbnr"
if b != board.String() {
t.Fatalf("expected board string %s but got %s", b, board.String())
}
board = board.Flip(UpDown)
b = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR"
if b != board.String() {
t.Fatalf("expected board string %s but got %s", b, board.String())
}
board = board.Flip(LeftRight)
b = "rnbkqbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBKQBNR"
if b != board.String() {
t.Fatalf("expected board string %s but got %s", b, board.String())
}
board = board.Flip(LeftRight)
b = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR"
if b != board.String() {
t.Fatalf("expected board string %s but got %s", b, board.String())
}
}
func TestBoardTranspose(t *testing.T) {
g := NewGame()
board := g.Position().Board()
board = board.Transpose()
b := "rp4PR/np4PN/bp4PB/qp4PQ/kp4PK/bp4PB/np4PN/rp4PR"
if b != board.String() {
t.Fatalf("expected board string %s but got %s", b, board.String())
}
}

50
doc.go

@ -0,0 +1,50 @@ @@ -0,0 +1,50 @@
/*
Package chess is a go library designed to accomplish the following:
- chess game / turn management
- move validation
- PGN encoding / decoding
- FEN encoding / decoding
Using Moves
game := chess.NewGame()
moves := game.ValidMoves()
game.Move(moves[0])
Using Algebraic Notation
game := chess.NewGame()
game.MoveStr("e4")
Using PGN
pgn, _ := chess.PGN(pgnReader)
game := chess.NewGame(pgn)
Using FEN
fen, _ := chess.FEN("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1")
game := chess.NewGame(fen)
Random Game
package main
import (
"fmt"
"math/rand"
"github.com/notnil/chess"
)
func main() {
game := chess.NewGame()
// generate moves until game is over
for game.Outcome() == chess.NoOutcome {
// select a random move
moves := game.ValidMoves()
move := moves[rand.Intn(len(moves))]
game.Move(move)
}
// print outcome and game PGN
fmt.Println(game.Position().Board().Draw())
fmt.Printf("Game completed. %s by %s.\n", game.Outcome(), game.Method())
fmt.Println(game.String())
}
*/
package chess

336
engine.go

@ -0,0 +1,336 @@ @@ -0,0 +1,336 @@
package chess
type engine struct{}
func (engine) CalcMoves(pos *Position, first bool) []*Move {
// generate possible moves
moves := standardMoves(pos, first)
// return moves including castles
return append(moves, castleMoves(pos)...)
}
func (engine) Status(pos *Position) Method {
hasMove := false
if pos.validMoves != nil {
hasMove = len(pos.validMoves) > 0
} else {
hasMove = len(engine{}.CalcMoves(pos, true)) > 0
}
if !pos.inCheck && !hasMove {
return Stalemate
} else if pos.inCheck && !hasMove {
return Checkmate
}
return NoMethod
}
var (
promoPieceTypes = []PieceType{Queen, Rook, Bishop, Knight}
)
func standardMoves(pos *Position, first bool) []*Move {
// compute allowed destination bitboard
bbAllowed := ^pos.board.whiteSqs
if pos.Turn() == Black {
bbAllowed = ^pos.board.blackSqs
}
moves := []*Move{}
// iterate through pieces to find possible moves
for _, p := range allPieces {
if pos.Turn() != p.Color() {
continue
}
// iterate through possible starting squares for piece
s1BB := pos.board.bbForPiece(p)
if s1BB == 0 {
continue
}
for s1 := 0; s1 < numOfSquaresInBoard; s1++ {
if s1BB&bbForSquare(Square(s1)) == 0 {
continue
}
// iterate through possible destination squares for piece
s2BB := bbForPossibleMoves(pos, p.Type(), Square(s1)) & bbAllowed
if s2BB == 0 {
continue
}
for s2 := 0; s2 < numOfSquaresInBoard; s2++ {
if s2BB&bbForSquare(Square(s2)) == 0 {
continue
}
// add promotions if pawn on promo square
if (p == WhitePawn && Square(s2).Rank() == Rank8) || (p == BlackPawn && Square(s2).Rank() == Rank1) {
for _, pt := range promoPieceTypes {
m := &Move{s1: Square(s1), s2: Square(s2), promo: pt}
addTags(m, pos)
// filter out moves that put king into check
if !m.HasTag(inCheck) {
moves = append(moves, m)
if first {
return moves
}
}
}
} else {
m := &Move{s1: Square(s1), s2: Square(s2)}
addTags(m, pos)
// filter out moves that put king into check
if !m.HasTag(inCheck) {
moves = append(moves, m)
if first {
return moves
}
}
}
}
}
}
return moves
}
func addTags(m *Move, pos *Position) {
p := pos.board.Piece(m.s1)
if pos.board.isOccupied(m.s2) {
m.addTag(Capture)
} else if m.s2 == pos.enPassantSquare && p.Type() == Pawn {
m.addTag(EnPassant)
}
// determine if in check after move (makes move invalid)
cp := pos.copy()
cp.board.update(m)
if isInCheck(cp) {
m.addTag(inCheck)
}
// determine if opponent in check after move
cp.turn = cp.turn.Other()
if isInCheck(cp) {
m.addTag(Check)
}
}
func isInCheck(pos *Position) bool {
kingSq := pos.board.whiteKingSq
if pos.Turn() == Black {
kingSq = pos.board.blackKingSq
}
// king should only be missing in tests / examples
if kingSq == NoSquare {
return false
}
return squaresAreAttacked(pos, kingSq)
}
func squaresAreAttacked(pos *Position, sqs ...Square) bool {
otherColor := pos.Turn().Other()
occ := ^pos.board.emptySqs
for _, sq := range sqs {
// hot path check to see if attack vector is possible
s2BB := pos.board.blackSqs
if pos.Turn() == Black {
s2BB = pos.board.whiteSqs
}
if ((diaAttack(occ, sq)|hvAttack(occ, sq))&s2BB)|(bbKnightMoves[sq]&s2BB) == 0 {
continue
}
// check queen attack vector
queenBB := pos.board.bbForPiece(getPiece(Queen, otherColor))
bb := (diaAttack(occ, sq) | hvAttack(occ, sq)) & queenBB
if bb != 0 {
return true
}
// check rook attack vector
rookBB := pos.board.bbForPiece(getPiece(Rook, otherColor))
bb = hvAttack(occ, sq) & rookBB
if bb != 0 {
return true
}
// check bishop attack vector
bishopBB := pos.board.bbForPiece(getPiece(Bishop, otherColor))
bb = diaAttack(occ, sq) & bishopBB
if bb != 0 {
return true
}
// check knight attack vector
knightBB := pos.board.bbForPiece(getPiece(Knight, otherColor))
bb = bbKnightMoves[sq] & knightBB
if bb != 0 {
return true
}
// check pawn attack vector
if pos.Turn() == White {
capRight := (pos.board.bbBlackPawn & ^bbFileH & ^bbRank1) << 7
capLeft := (pos.board.bbBlackPawn & ^bbFileA & ^bbRank1) << 9
bb = (capRight | capLeft) & bbForSquare(sq)
if bb != 0 {
return true
}
} else {
capRight := (pos.board.bbWhitePawn & ^bbFileH & ^bbRank8) >> 9
capLeft := (pos.board.bbWhitePawn & ^bbFileA & ^bbRank8) >> 7
bb = (capRight | capLeft) & bbForSquare(sq)
if bb != 0 {
return true
}
}
// check king attack vector
kingBB := pos.board.bbForPiece(getPiece(King, otherColor))
bb = bbKingMoves[sq] & kingBB
if bb != 0 {
return true
}
}
return false
}
func bbForPossibleMoves(pos *Position, pt PieceType, sq Square) bitboard {
switch pt {
case King:
return bbKingMoves[sq]
case Queen:
return diaAttack(^pos.board.emptySqs, sq) | hvAttack(^pos.board.emptySqs, sq)
case Rook:
return hvAttack(^pos.board.emptySqs, sq)
case Bishop:
return diaAttack(^pos.board.emptySqs, sq)
case Knight:
return bbKnightMoves[sq]
case Pawn:
return pawnMoves(pos, sq)
}
return bitboard(0)
}
// TODO can calc isInCheck twice
func castleMoves(pos *Position) []*Move {
moves := []*Move{}
kingSide := pos.castleRights.CanCastle(pos.Turn(), KingSide)
queenSide := pos.castleRights.CanCastle(pos.Turn(), QueenSide)
// white king side
if pos.turn == White && kingSide &&
(^pos.board.emptySqs&(bbForSquare(F1)|bbForSquare(G1))) == 0 &&
!squaresAreAttacked(pos, F1, G1) &&
!pos.inCheck {
m := &Move{s1: E1, s2: G1}
m.addTag(KingSideCastle)
moves = append(moves, m)
}
// white queen side
if pos.turn == White && queenSide &&
(^pos.board.emptySqs&(bbForSquare(B1)|bbForSquare(C1)|bbForSquare(D1))) == 0 &&
!squaresAreAttacked(pos, C1, D1) &&
!pos.inCheck {
m := &Move{s1: E1, s2: C1}
m.addTag(QueenSideCastle)
moves = append(moves, m)
}
// black king side
if pos.turn == Black && kingSide &&
(^pos.board.emptySqs&(bbForSquare(F8)|bbForSquare(G8))) == 0 &&
!squaresAreAttacked(pos, F8, G8) &&
!pos.inCheck {
m := &Move{s1: E8, s2: G8}
m.addTag(KingSideCastle)
moves = append(moves, m)
}
// black queen side
if pos.turn == Black && queenSide &&
(^pos.board.emptySqs&(bbForSquare(B8)|bbForSquare(C8)|bbForSquare(D8))) == 0 &&
!squaresAreAttacked(pos, C8, D8) &&
!pos.inCheck {
m := &Move{s1: E8, s2: C8}
m.addTag(QueenSideCastle)
moves = append(moves, m)
}
return moves
}
func pawnMoves(pos *Position, sq Square) bitboard {
bb := bbForSquare(sq)
var bbEnPassant bitboard
if pos.enPassantSquare != NoSquare {
bbEnPassant = bbForSquare(pos.enPassantSquare)
}
if pos.Turn() == White {
capRight := ((bb & ^bbFileH & ^bbRank8) >> 9) & (pos.board.blackSqs | bbEnPassant)
capLeft := ((bb & ^bbFileA & ^bbRank8) >> 7) & (pos.board.blackSqs | bbEnPassant)
upOne := ((bb & ^bbRank8) >> 8) & pos.board.emptySqs
upTwo := ((upOne & bbRank3) >> 8) & pos.board.emptySqs
return capRight | capLeft | upOne | upTwo
}
capRight := ((bb & ^bbFileH & ^bbRank1) << 7) & (pos.board.whiteSqs | bbEnPassant)
capLeft := ((bb & ^bbFileA & ^bbRank1) << 9) & (pos.board.whiteSqs | bbEnPassant)
upOne := ((bb & ^bbRank1) << 8) & pos.board.emptySqs
upTwo := ((upOne & bbRank6) << 8) & pos.board.emptySqs
return capRight | capLeft | upOne | upTwo
}
func diaAttack(occupied bitboard, sq Square) bitboard {
pos := bbForSquare(sq)
dMask := bbDiagonals[sq]
adMask := bbAntiDiagonals[sq]
return linearAttack(occupied, pos, dMask) | linearAttack(occupied, pos, adMask)
}
func hvAttack(occupied bitboard, sq Square) bitboard {
pos := bbForSquare(sq)
rankMask := bbRanks[Square(sq).Rank()]
fileMask := bbFiles[Square(sq).File()]
return linearAttack(occupied, pos, rankMask) | linearAttack(occupied, pos, fileMask)
}
func linearAttack(occupied, pos, mask bitboard) bitboard {
oInMask := occupied & mask
return ((oInMask - 2*pos) ^ (oInMask.Reverse() - 2*pos.Reverse()).Reverse()) & mask
}
const (
bbFileA bitboard = 9259542123273814144
bbFileB bitboard = 4629771061636907072
bbFileC bitboard = 2314885530818453536
bbFileD bitboard = 1157442765409226768
bbFileE bitboard = 578721382704613384
bbFileF bitboard = 289360691352306692
bbFileG bitboard = 144680345676153346
bbFileH bitboard = 72340172838076673
bbRank1 bitboard = 18374686479671623680
bbRank2 bitboard = 71776119061217280
bbRank3 bitboard = 280375465082880
bbRank4 bitboard = 1095216660480
bbRank5 bitboard = 4278190080
bbRank6 bitboard = 16711680
bbRank7 bitboard = 65280
bbRank8 bitboard = 255
)
// TODO make method on Square
func bbForSquare(sq Square) bitboard {
return bbSquares[sq]
}
var (
bbFiles = [8]bitboard{bbFileA, bbFileB, bbFileC, bbFileD, bbFileE, bbFileF, bbFileG, bbFileH}
bbRanks = [8]bitboard{bbRank1, bbRank2, bbRank3, bbRank4, bbRank5, bbRank6, bbRank7, bbRank8}
bbDiagonals = [64]bitboard{9241421688590303745, 4620710844295151872, 2310355422147575808, 1155177711073755136, 577588855528488960, 288794425616760832, 144396663052566528, 72057594037927936, 36099303471055874, 9241421688590303745, 4620710844295151872, 2310355422147575808, 1155177711073755136, 577588855528488960, 288794425616760832, 144396663052566528, 141012904183812, 36099303471055874, 9241421688590303745, 4620710844295151872, 2310355422147575808, 1155177711073755136, 577588855528488960, 288794425616760832, 550831656968, 141012904183812, 36099303471055874, 9241421688590303745, 4620710844295151872, 2310355422147575808, 1155177711073755136, 577588855528488960, 2151686160, 550831656968, 141012904183812, 36099303471055874, 9241421688590303745, 4620710844295151872, 2310355422147575808, 1155177711073755136, 8405024, 2151686160, 550831656968, 141012904183812, 36099303471055874, 9241421688590303745, 4620710844295151872, 2310355422147575808, 32832, 8405024, 2151686160, 550831656968, 141012904183812, 36099303471055874, 9241421688590303745, 4620710844295151872, 128, 32832, 8405024, 2151686160, 550831656968, 141012904183812, 36099303471055874, 9241421688590303745}
bbAntiDiagonals = [64]bitboard{9223372036854775808, 4647714815446351872, 2323998145211531264, 1161999622361579520, 580999813328273408, 290499906672525312, 145249953336295424, 72624976668147840, 4647714815446351872, 2323998145211531264, 1161999622361579520, 580999813328273408, 290499906672525312, 145249953336295424, 72624976668147840, 283691315109952, 2323998145211531264, 1161999622361579520, 580999813328273408, 290499906672525312, 145249953336295424, 72624976668147840, 283691315109952, 1108169199648, 1161999622361579520, 580999813328273408, 290499906672525312, 145249953336295424, 72624976668147840, 283691315109952, 1108169199648, 4328785936, 580999813328273408, 290499906672525312, 145249953336295424, 72624976668147840, 283691315109952, 1108169199648, 4328785936, 16909320, 290499906672525312, 145249953336295424, 72624976668147840, 283691315109952, 1108169199648, 4328785936, 16909320, 66052, 145249953336295424, 72624976668147840, 283691315109952, 1108169199648, 4328785936, 16909320, 66052, 258, 72624976668147840, 283691315109952, 1108169199648, 4328785936, 16909320, 66052, 258, 1}
bbKnightMoves = [64]bitboard{9077567998918656, 4679521487814656, 38368557762871296, 19184278881435648, 9592139440717824, 4796069720358912, 2257297371824128, 1128098930098176, 2305878468463689728, 1152939783987658752, 9799982666336960512, 4899991333168480256, 2449995666584240128, 1224997833292120064, 576469569871282176, 288234782788157440, 4620693356194824192, 11533718717099671552, 5802888705324613632, 2901444352662306816, 1450722176331153408, 725361088165576704, 362539804446949376, 145241105196122112, 18049583422636032, 45053588738670592, 22667534005174272, 11333767002587136, 5666883501293568, 2833441750646784, 1416171111120896, 567348067172352, 70506185244672, 175990581010432, 88545054707712, 44272527353856, 22136263676928, 11068131838464, 5531918402816, 2216203387392, 275414786112, 687463207072, 345879119952, 172939559976, 86469779988, 43234889994, 21609056261, 8657044482, 1075839008, 2685403152, 1351090312, 675545156, 337772578, 168886289, 84410376, 33816580, 4202496, 10489856, 5277696, 2638848, 1319424, 659712, 329728, 132096}
bbBishopMoves = [64]bitboard{18049651735527937, 45053622886727936, 22667548931719168, 11334324221640704, 5667164249915392, 2833579985862656, 1416240237150208, 567382630219904, 4611756524879479810, 11529391036782871041, 5764696068147249408, 2882348036221108224, 1441174018118909952, 720587009051099136, 360293502378066048, 144117404414255168, 2323857683139004420, 1197958188344280066, 9822351133174399489, 4911175566595588352, 2455587783297826816, 1227793891648880768, 577868148797087808, 288793334762704928, 1161999073681608712, 581140276476643332, 326598935265674242, 9386671504487645697, 4693335752243822976, 2310639079102947392, 1155178802063085600, 577588851267340304, 580999811184992272, 290500455356698632, 145390965166737412, 108724279602332802, 9241705379636978241, 4620711952330133792, 2310355426409252880, 1155177711057110024, 290499906664153120, 145249955479592976, 72625527495610504, 424704217196612, 36100411639206946, 9241421692918565393, 4620710844311799048, 2310355422147510788, 145249953336262720, 72624976676520096, 283693466779728, 1659000848424, 141017232965652, 36099303487963146, 9241421688590368773, 4620710844295151618, 72624976668147712, 283691315142656, 1108177604608, 6480472064, 550848566272, 141012904249856, 36099303471056128, 9241421688590303744}
bbRookMoves = [64]bitboard{9187484529235886208, 13781085504453754944, 16077885992062689312, 17226286235867156496, 17800486357769390088, 18087586418720506884, 18231136449196065282, 18302911464433844481, 9259260648297103488, 4665518383679160384, 2368647251370188832, 1220211685215703056, 645993902138460168, 358885010599838724, 215330564830528002, 143553341945872641, 9259541023762186368, 4629910699613634624, 2315095537539358752, 1157687956502220816, 578984165983651848, 289632270724367364, 144956323094725122, 72618349279904001, 9259542118978846848, 4629771607097753664, 2314886351157207072, 1157443723186933776, 578722409201797128, 289361752209228804, 144681423712944642, 72341259464802561, 9259542123257036928, 4629771063767613504, 2314885534022901792, 1157442769150545936, 578721386714368008, 289360695496279044, 144680349887234562, 72340177082712321, 9259542123273748608, 4629771061645230144, 2314885530830970912, 1157442765423841296, 578721382720276488, 289360691368494084, 144680345692602882, 72340172854657281, 9259542123273813888, 4629771061636939584, 2314885530818502432, 1157442765409283856, 578721382704674568, 289360691352369924, 144680345676217602, 72340172838141441, 9259542123273814143, 4629771061636907199, 2314885530818453727, 1157442765409226991, 578721382704613623, 289360691352306939, 144680345676153597, 72340172838076926}
bbQueenMoves = [64]bitboard{9205534180971414145, 13826139127340482880, 16100553540994408480, 17237620560088797200, 17806153522019305480, 18090419998706369540, 18232552689433215490, 18303478847064064385, 13871017173176583298, 16194909420462031425, 8133343319517438240, 4102559721436811280, 2087167920257370120, 1079472019650937860, 575624067208594050, 287670746360127809, 11583398706901190788, 5827868887957914690, 12137446670713758241, 6068863523097809168, 3034571949281478664, 1517426162373248132, 722824471891812930, 361411684042608929, 10421541192660455560, 5210911883574396996, 2641485286422881314, 10544115227674579473, 5272058161445620104, 2600000831312176196, 1299860225776030242, 649930110732142865, 9840541934442029200, 4920271519124312136, 2460276499189639204, 1266167048752878738, 9820426766351346249, 4910072647826412836, 2455035776296487442, 1227517888139822345, 9550042029937901728, 4775021017124823120, 2387511058326581416, 1157867469641037908, 614821794359483434, 9530782384287059477, 4765391190004401930, 2382695595002168069, 9404792076610076608, 4702396038313459680, 2315169224285282160, 1157444424410132280, 578862399937640220, 325459994840333070, 9386102034266586375, 4693051017133293059, 9332167099941961855, 4630054752952049855, 2314886638996058335, 1157442771889699055, 578721933553179895, 289501704256556795, 180779649147209725, 9313761861428380670}
bbKingMoves = [64]bitboard{4665729213955833856, 11592265440851656704, 5796132720425828352, 2898066360212914176, 1449033180106457088, 724516590053228544, 362258295026614272, 144959613005987840, 13853283560024178688, 16186183351374184448, 8093091675687092224, 4046545837843546112, 2023272918921773056, 1011636459460886528, 505818229730443264, 216739030602088448, 54114388906344448, 63227278716305408, 31613639358152704, 15806819679076352, 7903409839538176, 3951704919769088, 1975852459884544, 846636838289408, 211384331665408, 246981557485568, 123490778742784, 61745389371392, 30872694685696, 15436347342848, 7718173671424, 3307175149568, 825720045568, 964771708928, 482385854464, 241192927232, 120596463616, 60298231808, 30149115904, 12918652928, 3225468928, 3768639488, 1884319744, 942159872, 471079936, 235539968, 117769984, 50463488, 12599488, 14721248, 7360624, 3680312, 1840156, 920078, 460039, 197123, 49216, 57504, 28752, 14376, 7188, 3594, 1797, 770}
bbSquares = [64]bitboard{}
)
func init() {
for sq := 0; sq < 64; sq++ {
bbSquares[sq] = bitboard(uint64(1) << (uint8(63) - uint8(sq)))
}
}

BIN
example.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

155
fen.go

@ -0,0 +1,155 @@ @@ -0,0 +1,155 @@
package chess
import (
"fmt"
"strconv"
"strings"
)
// Decodes FEN notation into a GameState. An error is returned
// if there is a parsing error. FEN notation format:
// rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1
func decodeFEN(fen string) (*Position, error) {
fen = strings.TrimSpace(fen)
parts := strings.Split(fen, " ")
if len(parts) != 6 {
return nil, fmt.Errorf("chess: fen invalid notiation %s must have 6 sections", fen)
}
b, err := fenBoard(parts[0])
if err != nil {
return nil, err
}
turn, ok := fenTurnMap[parts[1]]
if !ok {
return nil, fmt.Errorf("chess: fen invalid turn %s", parts[1])
}
rights, err := formCastleRights(parts[2])
if err != nil {
return nil, err
}
sq, err := formEnPassant(parts[3])
if err != nil {
return nil, err
}
halfMoveClock, err := strconv.Atoi(parts[4])
if err != nil || halfMoveClock < 0 {
return nil, fmt.Errorf("chess: fen invalid half move clock %s", parts[4])
}
moveCount, err := strconv.Atoi(parts[5])
if err != nil || moveCount < 1 {
return nil, fmt.Errorf("chess: fen invalid move count %s", parts[5])
}
return &Position{
board: b,
turn: turn,
castleRights: rights,
enPassantSquare: sq,
halfMoveClock: halfMoveClock,
moveCount: moveCount,
}, nil
}
// generates board from fen format: rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR
func fenBoard(boardStr string) (*Board, error) {
rankStrs := strings.Split(boardStr, "/")
if len(rankStrs) != 8 {
return nil, fmt.Errorf("chess: fen invalid board %s", boardStr)
}
m := map[Square]Piece{}
for i, rankStr := range rankStrs {
rank := Rank(7 - i)
fileMap, err := fenFormRank(rankStr)
if err != nil {
return nil, err
}
for file, piece := range fileMap {
m[getSquare(file, rank)] = piece
}
}
return NewBoard(m), nil
}
func fenFormRank(rankStr string) (map[File]Piece, error) {
count := 0
m := map[File]Piece{}
err := fmt.Errorf("chess: fen invalid rank %s", rankStr)
for _, r := range rankStr {
c := fmt.Sprintf("%c", r)
piece := fenPieceMap[c]
if piece == NoPiece {
skip, err := strconv.Atoi(c)
if err != nil {
return nil, err
}
count += skip
continue
}
m[File(count)] = piece
count++
}
if count != 8 {
return nil, err
}
return m, nil
}
func formCastleRights(castleStr string) (CastleRights, error) {
// check for duplicates aka. KKkq right now is valid
for _, s := range []string{"K", "Q", "k", "q", "-"} {
if strings.Count(castleStr, s) > 1 {
return "-", fmt.Errorf("chess: fen invalid castle rights %s", castleStr)
}
}
for _, r := range castleStr {
c := fmt.Sprintf("%c", r)
switch c {
case "K", "Q", "k", "q", "-":
default:
return "-", fmt.Errorf("chess: fen invalid castle rights %s", castleStr)
}
}
return CastleRights(castleStr), nil
}
func formEnPassant(enPassant string) (Square, error) {
if enPassant == "-" {
return NoSquare, nil
}
sq := strToSquareMap[enPassant]
if sq == NoSquare || !(sq.Rank() == Rank3 || sq.Rank() == Rank6) {
return NoSquare, fmt.Errorf("chess: fen invalid En Passant square %s", enPassant)
}
return sq, nil
}
var (
fenSkipMap = map[int]string{
1: "1",
2: "2",
3: "3",
4: "4",
5: "5",
6: "6",
7: "7",
8: "8",
}
fenPieceMap = map[string]Piece{
"K": WhiteKing,
"Q": WhiteQueen,
"R": WhiteRook,
"B": WhiteBishop,
"N": WhiteKnight,
"P": WhitePawn,
"k": BlackKing,
"q": BlackQueen,
"r": BlackRook,
"b": BlackBishop,
"n": BlackKnight,
"p": BlackPawn,
}
fenTurnMap = map[string]Color{
"w": White,
"b": Black,
}
)

54
fen_test.go

@ -0,0 +1,54 @@ @@ -0,0 +1,54 @@
package chess
import "testing"
var (
validFENs = []string{
"rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1",
"rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR b KQkq e3 0 1",
"rnbqkbnr/pp1ppppp/8/2p5/4P3/8/PPPP1PPP/RNBQKBNR w KQkq c6 0 2",
"rnbqkbnr/pp1ppppp/8/2p5/4P3/5N2/PPPP1PPP/RNBQKB1R b KQkq - 1 2",
"7k/8/8/8/8/8/8/R6K w - - 0 1",
"7k/8/8/8/8/8/8/2B1KB2 w - - 0 1",
"8/8/8/4k3/8/8/8/R3K2R w KQ - 0 1",
"8/8/8/8/4k3/8/3KP3/8 w - - 0 1",
"8/8/5k2/8/5K2/8/4P3/8 w - - 0 1",
"r4rk1/1b2bppp/ppq1p3/2pp3n/5P2/1P1BP3/PBPPQ1PP/R4RK1 w - - 0 1",
"3r1rk1/p3qppp/2bb4/2p5/3p4/1P2P3/PBQN1PPP/2R2RK1 w - - 0 1",
"4r1k1/1b3p1p/ppq3p1/2p5/8/1P3R1Q/PBP3PP/7K w - - 0 1",
"5k2/ppp5/4P3/3R3p/6P1/1K2Nr2/PP3P2/8 b - - 1 32",
}
invalidFENs = []string{
"rnbqkbnr/pppppppp/8/8/8/8/PPPPPPP/RNBQKBNR w KQkq - 0 1",
"rnbqkbnr/pppppppp/8/8/4P2/8/PPPP1PPP/RNBQKBNR b KQkq e3 0 1",
"rnbqkbnr/pp1ppppp/8/2p5/4P3/8/PPPP1PPP/RNBQKBNR w KKkq c6 0 2",
"rnbqkbnr/pp1ppppp/8/2p5/4P3/5N2/PPPP1PPP/RNBQKB1R b KQkq c12 1 2",
"7k/8/8/8/8/8/8/R6K w - - 0 -1",
"7k/8/8/8/8/8/8/2B1KB2 w - - -1 1",
"8/8/8/4k3/8/8/8/R3K2R w KQ - 0 0",
"8/8/8/8/4k3/8/3KP3/8 c - - 0 1",
"8/8/5k2/8/5K2/8/4P3P/8 w - - 0 1",
"r4rk1/1b2bppp/ppq1p3/2pp3n/5P2/1P1BP3/PBPPQ1PP/R4RK1 w e4 - 0 1",
}
)
func TestValidFENs(t *testing.T) {
for _, f := range validFENs {
state, err := decodeFEN(f)
if err != nil {
t.Fatal("recieved unexpected error", err)
}
if f != state.String() {
t.Fatalf("fen expected board string %s but got %s", f, state.String())
}
}
}
func TestInvalidFENs(t *testing.T) {
for _, f := range invalidFENs {
if _, err := decodeFEN(f); err == nil {
t.Fatal("fen expected error from ", f)
}
}
}

402
game.go

@ -0,0 +1,402 @@ @@ -0,0 +1,402 @@
package chess
import (
"errors"
"fmt"
"io"
"io/ioutil"
)
const (
startFEN = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"
)
// A Outcome is the result of a game.
type Outcome string
const (
// NoOutcome indicates that a game is in progress or ended without a result.
NoOutcome Outcome = "*"
// WhiteWon indicates that white won the game.
WhiteWon Outcome = "1-0"
// BlackWon indicates that black won the game.
BlackWon Outcome = "0-1"
// Draw indicates that game was a draw.
Draw Outcome = "1/2-1/2"
)
// String implements the fmt.Stringer interface
func (o Outcome) String() string {
return string(o)
}
// A Method is the method that generated the outcome.
type Method uint8
const (
// NoMethod indicates that an outcome hasn't occurred or that the method can't be determined.
NoMethod Method = iota
// Checkmate indicates that the game was won checkmate.
Checkmate
// Resignation indicates that the game was won by resignation.
Resignation
// DrawOffer indicates that the game was drawn by a draw offer.
DrawOffer
// Stalemate indicates that the game was drawn by stalemate.
Stalemate
// ThreefoldRepetition indicates that the game was drawn when the game
// state was repeated three times and a player requested a draw.
ThreefoldRepetition
// FivefoldRepetition indicates that the game was automatically drawn
// by the game state being repeated five times.
FivefoldRepetition
// FiftyMoveRule indicates that the game was drawn by the half
// move clock being one hundred or greater when a player requested a draw.
FiftyMoveRule
// SeventyFiveMoveRule indicates that the game was automatically drawn
// when the half move clock was one hundred and fifty or greater.
SeventyFiveMoveRule
// InsufficientMaterial indicates that the game was automatically drawn
// because there was insufficient material for checkmate.
InsufficientMaterial
)
// TagPair represents metadata in a key value pairing used in the PGN format.
type TagPair struct {
Key string
Value string
}
// A Game represents a single chess game.
type Game struct {
Notation Notation
TagPairs []*TagPair
Moves []*Move
Positions []*Position
Pos *Position
Outcome Outcome
Method Method
IgnoreAutomaticDraws bool
}
// PGN takes a reader and returns a function that updates
// the game to reflect the PGN data. The PGN can use any
// move notation supported by this package. The returned
// function is designed to be used in the NewGame constructor.
// An error is returned if there is a problem parsing the PGN data.
func PGN(r io.Reader) (func(*Game), error) {
b, err := ioutil.ReadAll(r)
if err != nil {
return nil, err
}
game, err := decodePGN(string(b))
if err != nil {
return nil, err
}
return func(g *Game) {
g.copy(game)
}, nil
}
// FEN takes a string and returns a function that updates
// the game to reflect the FEN data. Since FEN doesn't encode
// prior moves, the move list will be empty. The returned
// function is designed to be used in the NewGame constructor.
// An error is returned if there is a problem parsing the FEN data.
func FEN(fen string) (func(*Game), error) {
pos, err := decodeFEN(fen)
if err != nil {
return nil, err
}
return func(g *Game) {
pos.inCheck = isInCheck(pos)
g.Pos = pos
g.Positions = []*Position{pos}
g.updatePosition()
}, nil
}
// TagPairs returns a function that sets the tag pairs
// to the given value. The returned function is designed
// to be used in the NewGame constructor.
func TagPairs(tagPairs []*TagPair) func(*Game) {
return func(g *Game) {
g.TagPairs = append([]*TagPair(nil), tagPairs...)
}
}
// UseNotation returns a function that sets the game's notation
// to the given value. The notation is used to parse the
// string supplied to the MoveStr() method as well as the
// any PGN output. The returned function is designed
// to be used in the NewGame constructor.
func UseNotation(n Notation) func(*Game) {
return func(g *Game) {
g.Notation = n
}
}
// NewGame defaults to returning a game in the standard
// opening position. Options can be given to configure
// the game's initial state.
func NewGame(options ...func(*Game)) *Game {
pos, _ := decodeFEN(startFEN)
game := &Game{
Notation: AlgebraicNotation{},
Moves: []*Move{},
Pos: pos,
Positions: []*Position{pos},
Outcome: NoOutcome,
Method: NoMethod,
}
for _, f := range options {
if f != nil {
f(game)
}
}
return game
}
// Move updates the game with the given move. An error is returned
// if the move is invalid or the game has already been completed.
func (g *Game) Move(m *Move) error {
valid := moveSlice(g.ValidMoves()).find(m)
if valid == nil {
return fmt.Errorf("chess: invalid move %s", m)
}
g.Moves = append(g.Moves, valid)
g.Pos = g.Pos.Update(valid)
g.Positions = append(g.Positions, g.Pos)
g.updatePosition()
return nil
}
// MoveStr decodes the given string in game's notation
// and calls the Move function. An error is returned if
// the move can't be decoded or the move is invalid.
func (g *Game) MoveStr(s string) error {
m, err := g.Notation.Decode(g.Pos, s)
if err != nil {
return err
}
return g.Move(m)
}
// ValidMoves returns a list of valid moves in the
// current position.
func (g *Game) ValidMoves() []*Move {
return g.Pos.ValidMoves()
}
// PositionsHistory returns the position history of the game.
func (g *Game) PositionsHistory() []*Position {
return append([]*Position(nil), g.Positions...)
}
// MovesHistory returns the move history of the game.
func (g *Game) MovesHistory() []*Move {
return append([]*Move(nil), g.Moves...)
}
// GetTagPairs returns the game's tag pairs.
func (g *Game) GetTagPairs() []*TagPair {
return append([]*TagPair(nil), g.TagPairs...)
}
// Position returns the game's current position.
func (g *Game) Position() *Position {
return g.Pos
}
// GameOutcome returns the game outcome.
func (g *Game) GameOutcome() Outcome {
return g.Outcome
}
// OutcomeMethod returns the method in which the outcome occurred.
func (g *Game) OutcomeMethod() Method {
return g.Method
}
// FEN returns the FEN notation of the current position.
func (g *Game) FEN() string {
return g.Pos.String()
}
// String implements the fmt.Stringer interface and returns
// the game's PGN.
func (g *Game) String() string {
return encodePGN(g)
}
// MarshalText implements the encoding.TextMarshaler interface and
// encodes the game's PGN.
func (g *Game) MarshalText() (text []byte, err error) {
return []byte(encodePGN(g)), nil
}
// UnmarshalText implements the encoding.TextUnarshaler interface and
// assumes the data is in the PGN format.
func (g *Game) UnmarshalText(text []byte) error {
game, err := decodePGN(string(text))
if err != nil {
return err
}
g.copy(game)
return nil
}
// Draw attempts to draw the game by the given method. If the
// method is valid, then the game is updated to a draw by that
// method. If the method isn't valid then an error is returned.
func (g *Game) Draw(method Method) error {
switch method {
case ThreefoldRepetition:
if g.numOfRepitions() < 3 {
return errors.New("chess: draw by ThreefoldRepetition requires at least three repetitions of the current board state")
}
case FiftyMoveRule:
if g.Pos.halfMoveClock < 100 {
return fmt.Errorf("chess: draw by FiftyMoveRule requires the half move clock to be at 100 or greater but is %d", g.Pos.halfMoveClock)
}
case DrawOffer:
default:
return fmt.Errorf("chess: unsupported draw method %s", method.String())
}
g.Outcome = Draw
g.Method = method
return nil
}
// Resign resigns the game for the given color. If the game has
// already been completed then the game is not updated.
func (g *Game) Resign(color Color) {
if g.Outcome != NoOutcome || color == NoColor {
return
}
if color == White {
g.Outcome = BlackWon
} else {
g.Outcome = WhiteWon
}
g.Method = Resignation
}
// EligibleDraws returns valid inputs for the Draw() method.
func (g *Game) EligibleDraws() []Method {
draws := []Method{DrawOffer}
if g.numOfRepitions() >= 3 {
draws = append(draws, ThreefoldRepetition)
}
if g.Pos.halfMoveClock >= 100 {
draws = append(draws, FiftyMoveRule)
}
return draws
}
// AddTagPair adds or updates a tag pair with the given key and
// value and returns true if the value is overwritten.
func (g *Game) AddTagPair(k, v string) bool {
for i, tag := range g.TagPairs {
if tag.Key == k {
g.TagPairs[i].Value = v
return true
}
}
g.TagPairs = append(g.TagPairs, &TagPair{Key: k, Value: v})
return false
}
// GetTagPair returns the tag pair for the given key or nil
// if it is not present.
func (g *Game) GetTagPair(k string) *TagPair {
for _, tag := range g.TagPairs {
if tag.Key == k {
return tag
}
}
return nil
}
// RemoveTagPair removes the tag pair for the given key and
// returns true if a tag pair was removed.
func (g *Game) RemoveTagPair(k string) bool {
cp := []*TagPair{}
found := false
for _, tag := range g.TagPairs {
if tag.Key == k {
found = true
} else {
cp = append(cp, tag)
}
}
g.TagPairs = cp
return found
}
func (g *Game) updatePosition() {
method := g.Pos.Status()
if method == Stalemate {
g.Method = Stalemate
g.Outcome = Draw
} else if method == Checkmate {
g.Method = Checkmate
g.Outcome = WhiteWon
if g.Pos.Turn() == White {
g.Outcome = BlackWon
}
}
if g.Outcome != NoOutcome {
return
}
// five fold rep creates automatic draw
if !g.IgnoreAutomaticDraws && g.numOfRepitions() >= 5 {
g.Outcome = Draw
g.Method = FivefoldRepetition
}
// 75 move rule creates automatic draw
if !g.IgnoreAutomaticDraws && g.Pos.halfMoveClock >= 150 && g.Method != Checkmate {
g.Outcome = Draw
g.Method = SeventyFiveMoveRule
}
// insufficient material creates automatic draw
if !g.IgnoreAutomaticDraws && !g.Pos.board.hasSufficientMaterial() {
g.Outcome = Draw
g.Method = InsufficientMaterial
}
}
func (g *Game) copy(game *Game) {
g.TagPairs = game.GetTagPairs()
g.Moves = game.MovesHistory()
g.Positions = game.PositionsHistory()
g.Pos = game.Pos
g.Outcome = game.Outcome
g.Method = game.Method
}
// Clone clones a game
func (g *Game) Clone() *Game {
return &Game{
TagPairs: g.GetTagPairs(),
Notation: g.Notation,
Moves: g.MovesHistory(),
Positions: g.PositionsHistory(),
Pos: g.Pos,
Outcome: g.Outcome,
Method: g.Method,
}
}
func (g *Game) numOfRepitions() int {
count := 0
for _, pos := range g.PositionsHistory() {
if g.Pos.samePosition(pos) {
count++
}
}
return count
}

292
game_test.go

@ -0,0 +1,292 @@ @@ -0,0 +1,292 @@
package chess
import (
"log"
"strings"
"testing"
)
func TestCheckmate(t *testing.T) {
fenStr := "rn1qkbnr/pbpp1ppp/1p6/4p3/2B1P3/5Q2/PPPP1PPP/RNB1K1NR w KQkq - 0 1"
fen, err := FEN(fenStr)
if err != nil {
t.Fatal(err)
}
g := NewGame(fen)
if err := g.MoveStr("Qxf7#"); err != nil {
t.Fatal(err)
}
if g.Method() != Checkmate {
t.Fatalf("expected method %s but got %s", Checkmate, g.Method())
}
if g.Outcome() != WhiteWon {
t.Fatalf("expected outcome %s but got %s", WhiteWon, g.Outcome())
}
}
func TestCheckmateFromFen(t *testing.T) {
fenStr := "rn1qkbnr/pbpp1Qpp/1p6/4p3/2B1P3/8/PPPP1PPP/RNB1K1NR b KQkq - 0 1"
fen, err := FEN(fenStr)
if err != nil {
t.Fatal(err)
}
g := NewGame(fen)
if g.Method() != Checkmate {
t.Error(g.Position().Board().Draw())
t.Fatalf("expected method %s but got %s", Checkmate, g.Method())
}
if g.Outcome() != WhiteWon {
t.Fatalf("expected outcome %s but got %s", WhiteWon, g.Outcome())
}
}
func TestStalemate(t *testing.T) {
fenStr := "k1K5/8/8/8/8/8/8/1Q6 w - - 0 1"
fen, err := FEN(fenStr)
if err != nil {
t.Fatal(err)
}
g := NewGame(fen)
if err := g.MoveStr("Qb6"); err != nil {
t.Fatal(err)
}
if g.Method() != Stalemate {
t.Fatalf("expected method %s but got %s", Stalemate, g.Method())
}
if g.Outcome() != Draw {
t.Fatalf("expected outcome %s but got %s", Draw, g.Outcome())
}
}
// position shouldn't result in stalemate because pawn can move http://en.lichess.org/Pc6mJDZN#138
func TestInvalidStalemate(t *testing.T) {
fenStr := "8/3P4/8/8/8/7k/7p/7K w - - 2 70"
fen, err := FEN(fenStr)
if err != nil {
t.Fatal(err)
}
g := NewGame(fen)
if err := g.MoveStr("d8=Q"); err != nil {
t.Fatal(err)
}
if g.Outcome() != NoOutcome {
t.Fatalf("expected outcome %s but got %s", NoOutcome, g.Outcome())
}
}
func TestThreeFoldRepition(t *testing.T) {
g := NewGame()
moves := []string{
"Nf3", "Nf6", "Ng1", "Ng8",
"Nf3", "Nf6", "Ng1", "Ng8",
}
for _, m := range moves {
if err := g.MoveStr(m); err != nil {
t.Fatal(err)
}
}
if err := g.Draw(ThreefoldRepetition); err != nil {
for _, pos := range g.Positions() {
log.Println(pos.String())
}
t.Fatalf("%s - %d reps", err.Error(), g.numOfRepitions())
}
}
func TestInvalidThreeFoldRepition(t *testing.T) {
g := NewGame()
moves := []string{
"Nf3", "Nf6", "Ng1", "Ng8",
}
for _, m := range moves {
if err := g.MoveStr(m); err != nil {
t.Fatal(err)
}
}
if err := g.Draw(ThreefoldRepetition); err == nil {
t.Fatal("should require three repeated board states")
}
}
func TestFiveFoldRepition(t *testing.T) {
g := NewGame()
moves := []string{
"Nf3", "Nf6", "Ng1", "Ng8",
"Nf3", "Nf6", "Ng1", "Ng8",
"Nf3", "Nf6", "Ng1", "Ng8",
"Nf3", "Nf6", "Ng1", "Ng8",
}
for _, m := range moves {
if err := g.MoveStr(m); err != nil {
t.Fatal(err)
}
}
if g.Outcome() != Draw || g.Method() != FivefoldRepetition {
t.Fatal("should automatically draw after five repetitions")
}
}
func TestFiftyMoveRule(t *testing.T) {
fen, _ := FEN("2r3k1/1q1nbppp/r3p3/3pP3/pPpP4/P1Q2N2/2RN1PPP/2R4K b - b3 100 60")
g := NewGame(fen)
if err := g.Draw(FiftyMoveRule); err != nil {
t.Fatal(err)
}
}
func TestInvalidFiftyMoveRule(t *testing.T) {
fen, _ := FEN("2r3k1/1q1nbppp/r3p3/3pP3/pPpP4/P1Q2N2/2RN1PPP/2R4K b - b3 99 60")
g := NewGame(fen)
if err := g.Draw(FiftyMoveRule); err == nil {
t.Fatal("should require fifty moves")
}
}
func TestSeventyFiveMoveRule(t *testing.T) {
fen, _ := FEN("2r3k1/1q1nbppp/r3p3/3pP3/pPpP4/P1Q2N2/2RN1PPP/2R4K b - b3 149 80")
g := NewGame(fen)
if err := g.MoveStr("Kf8"); err != nil {
t.Fatal(err)
}
if g.Outcome() != Draw || g.Method() != SeventyFiveMoveRule {
t.Fatal("should automatically draw after seventy five moves w/ no pawn move or capture")
}
}
func TestInsufficentMaterial(t *testing.T) {
fens := []string{
"8/2k5/8/8/8/3K4/8/8 w - - 1 1",
"8/2k5/8/8/8/3K1N2/8/8 w - - 1 1",
"8/2k5/8/8/8/3K1B2/8/8 w - - 1 1",
"8/2k5/2b5/8/8/3K1B2/8/8 w - - 1 1",
"4b3/2k5/2b5/8/8/3K1B2/8/8 w - - 1 1",
}
for _, f := range fens {
fen, err := FEN(f)
if err != nil {
t.Fatal(err)
}
g := NewGame(fen)
if g.Outcome() != Draw || g.Method() != InsufficientMaterial {
log.Println(g.Position().Board().Draw())
t.Fatalf("%s should automatically draw by insufficent material", f)
}
}
}
func TestSufficentMaterial(t *testing.T) {
fens := []string{
"8/2k5/8/8/8/3K1B2/4N3/8 w - - 1 1",
"8/2k5/8/8/8/3KBB2/8/8 w - - 1 1",
"8/2k1b3/8/8/8/3K1B2/8/8 w - - 1 1",
"8/2k5/8/8/4P3/3K4/8/8 w - - 1 1",
"8/2k5/8/8/8/3KQ3/8/8 w - - 1 1",
"8/2k5/8/8/8/3KR3/8/8 w - - 1 1",
}
for _, f := range fens {
fen, err := FEN(f)
if err != nil {
t.Fatal(err)
}
g := NewGame(fen)
if g.Outcome() != NoOutcome {
log.Println(g.Position().Board().Draw())
t.Fatalf("%s should not find insufficent material", f)
}
}
}
func TestSerializationCycle(t *testing.T) {
g := NewGame()
pgn, err := PGN(strings.NewReader(g.String()))
if err != nil {
t.Fatal(err)
}
cp := NewGame(pgn)
if cp.String() != g.String() {
t.Fatalf("expected %s but got %s", g.String(), cp.String())
}
}
func TestInitialNumOfValidMoves(t *testing.T) {
g := NewGame()
if len(g.ValidMoves()) != 20 {
t.Fatal("should find 20 valid moves from the initial position")
}
}
func TestTagPairs(t *testing.T) {
g := NewGame()
g.AddTagPair("Draw Offer", "White")
tagPair := g.GetTagPair("Draw Offer")
if tagPair == nil {
t.Fatalf("expected %s but got %s", "White", "nil")
}
if tagPair.Value != "White" {
t.Fatalf("expected %s but got %s", "White", tagPair.Value)
}
g.RemoveTagPair("Draw Offer")
tagPair = g.GetTagPair("Draw Offer")
if tagPair != nil {
t.Fatalf("expected %s but got %s", "nil", "not nil")
}
}
func TestPositionHash(t *testing.T) {
g1 := NewGame()
for _, s := range []string{"Nc3", "e5", "Nf3"} {
g1.MoveStr(s)
}
g2 := NewGame()
for _, s := range []string{"Nf3", "e5", "Nc3"} {
g2.MoveStr(s)
}
if g1.Position().Hash() != g2.Position().Hash() {
t.Fatalf("expected position hashes to be equal but got %s and %s", g1.Position().Hash(), g2.Position().Hash())
}
}
func BenchmarkStalemateStatus(b *testing.B) {
fenStr := "k1K5/8/8/8/8/8/8/1Q6 w - - 0 1"
fen, err := FEN(fenStr)
if err != nil {
b.Fatal(err)
}
g := NewGame(fen)
if err := g.MoveStr("Qb6"); err != nil {
b.Fatal(err)
}
b.ResetTimer()
for n := 0; n < b.N; n++ {
g.Position().Status()
}
}
func BenchmarkInvalidStalemateStatus(b *testing.B) {
fenStr := "8/3P4/8/8/8/7k/7p/7K w - - 2 70"
fen, err := FEN(fenStr)
if err != nil {
b.Fatal(err)
}
g := NewGame(fen)
if err := g.MoveStr("d8=Q"); err != nil {
b.Fatal(err)
}
b.ResetTimer()
for n := 0; n < b.N; n++ {
g.Position().Status()
}
}
func BenchmarkPositionHash(b *testing.B) {
fenStr := "8/3P4/8/8/8/7k/7p/7K w - - 2 70"
fen, err := FEN(fenStr)
if err != nil {
b.Fatal(err)
}
g := NewGame(fen)
b.ResetTimer()
for n := 0; n < b.N; n++ {
g.Position().Hash()
}
}

5
go.mod

@ -0,0 +1,5 @@ @@ -0,0 +1,5 @@
module git.nightmare.haus/rudi/chessv2
go 1.15
require github.com/ajstarks/svgo v0.0.0-20200725142600-7a3c8b57fecb

2
go.sum

@ -0,0 +1,2 @@ @@ -0,0 +1,2 @@
github.com/ajstarks/svgo v0.0.0-20200725142600-7a3c8b57fecb h1:EVl3FJLQCzSbgBezKo/1A4ADnJ4mtJZ0RvnNzDJ44nY=
github.com/ajstarks/svgo v0.0.0-20200725142600-7a3c8b57fecb/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw=

BIN
image/.DS_Store vendored

Binary file not shown.

84
image/README.md

@ -0,0 +1,84 @@ @@ -0,0 +1,84 @@
# image
## Introduction
Image is an chess image utility that converts board positions into [SVG](https://en.wikipedia.org/wiki/Scalable_Vector_Graphics), or Scalable Vector Graphics, images. [svgo](https://github.com/ajstarks/svgo), the only outside dependency, is used to construct the SVG document.
## Usage
### SVG
The SVG function is the primary exported function of the package. It writes an SVG document to the io.Writer given.
```go
file, _ := os.Open("output.svg")
defer file.Close()
fenStr := "rnbqkbnr/pppppppp/8/8/3P4/8/PPP1PPPP/RNBQKBNR b KQkq - 0 1"
pos := &chess.Position{}
pos.UnmarshalText([]byte(fenStr))
image.SVG(file, pos.Board())
```
### Dark / Light Square Customization
The default colors, shown in the example SVG below, are (235, 209, 166) for light squares and (165, 117, 81) for dark squares. The light and dark squares can be customized using the SquareColors() option.
```go
white := color.RGBA{255, 255, 255, 1}
gray := color.RGBA{120, 120, 120, 1}
sqrs := image.SquareColors(white, gray)
image.SVG(file, pos.Board(), sqrs)
```
### Marked Squares
MarkSquares is designed to be used as an optional argument to the SVG function. It marks the given squares with the color. A possible usage includes marking squares of the previous move.
```go
yellow := color.RGBA{255, 255, 0, 1}
mark := image.MarkSquares(yellow, chess.D2, chess.D4)
image.SVG(file, pos.Board(), mark)
```
### Example Program
```go
package main
import (
"image/color"
"log"
"os"
"github.com/notnil/chess"
"github.com/notnil/chess/image"
)
func main() {
// create file
f, err := os.Create("example.svg")
if err != nil {
log.Fatal(err)
}
defer f.Close()
// create board position
fenStr := "rnbqkbnr/pppppppp/8/8/3P4/8/PPP1PPPP/RNBQKBNR b KQkq - 0 1"
pos := &chess.Position{}
if err := pos.UnmarshalText([]byte(fenStr)); err != nil {
log.Fatal(err)
}
// write board SVG to file
yellow := color.RGBA{255, 255, 0, 1}
mark := image.MarkSquares(yellow, chess.D2, chess.D4)
if err := image.SVG(f, pos.Board(), mark); err != nil {
log.Fatal(err)
}
}
```
### Example Program Result
![rnbqkbnr/pppppppp/8/8/3P4/8/PPP1PPPP/RNBQKBNR b KQkq - 0 1](/example.svg)

BIN
image/example.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

534
image/example.svg

@ -0,0 +1,534 @@ @@ -0,0 +1,534 @@
<?xml version="1.0"?>
<!-- Generated by SVGo -->
<svg width="360" height="360"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink">
<rect x="0" y="0" width="360" height="360" />
<rect x="0" y="315" width="45" height="45" style="fill: #a57551" />
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="360" height="360" viewBox="0 -315 360 360">
<g style="opacity:1; fill:#ffffff; fill-opacity:1; fill-rule:evenodd; stroke:#000000; stroke-width:1.5; stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4; stroke-dasharray:none; stroke-opacity:1;">
<path
d="M 9,39 L 36,39 L 36,36 L 9,36 L 9,39 z "
style="stroke-linecap:butt;" />
<path
d="M 12,36 L 12,32 L 33,32 L 33,36 L 12,36 z "
style="stroke-linecap:butt;" />
<path
d="M 11,14 L 11,9 L 15,9 L 15,11 L 20,11 L 20,9 L 25,9 L 25,11 L 30,11 L 30,9 L 34,9 L 34,14"
style="stroke-linecap:butt;" />
<path
d="M 34,14 L 31,17 L 14,17 L 11,14" />
<path
d="M 31,17 L 31,29.5 L 14,29.5 L 14,17"
style="stroke-linecap:butt; stroke-linejoin:miter;" />
<path
d="M 31,29.5 L 32.5,32 L 12.5,32 L 14,29.5" />
<path
d="M 11,14 L 34,14"
style="fill:none; stroke:#000000; stroke-linejoin:miter;" />
</g>
</svg>
<text x="2" y="326" style="font-size:11px;fill: #ebd1a6" >1</text>
<text x="42" y="357" style="text-anchor:end;font-size:11px;fill: #ebd1a6" >a</text>
<rect x="45" y="315" width="45" height="45" style="fill: #ebd1a6" />
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="360" height="360" viewBox="-45 -315 360 360">
<g style="opacity:1; fill:none; fill-opacity:1; fill-rule:evenodd; stroke:#000000; stroke-width:1.5; stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4; stroke-dasharray:none; stroke-opacity:1;">
<path
d="M 22,10 C 32.5,11 38.5,18 38,39 L 15,39 C 15,30 25,32.5 23,18"
style="fill:#ffffff; stroke:#000000;" />
<path
d="M 24,18 C 24.38,20.91 18.45,25.37 16,27 C 13,29 13.18,31.34 11,31 C 9.958,30.06 12.41,27.96 11,28 C 10,28 11.19,29.23 10,30 C 9,30 5.997,31 6,26 C 6,24 12,14 12,14 C 12,14 13.89,12.1 14,10.5 C 13.27,9.506 13.5,8.5 13.5,7.5 C 14.5,6.5 16.5,10 16.5,10 L 18.5,10 C 18.5,10 19.28,8.008 21,7 C 22,7 22,10 22,10"
style="fill:#ffffff; stroke:#000000;" />
<path
d="M 9.5 25.5 A 0.5 0.5 0 1 1 8.5,25.5 A 0.5 0.5 0 1 1 9.5 25.5 z"
style="fill:#000000; stroke:#000000;" />
<path
d="M 15 15.5 A 0.5 1.5 0 1 1 14,15.5 A 0.5 1.5 0 1 1 15 15.5 z"
transform="matrix(0.866,0.5,-0.5,0.866,9.693,-5.173)"
style="fill:#000000; stroke:#000000;" />
</g>
</svg>
<text x="87" y="357" style="text-anchor:end;font-size:11px;fill: #a57551" >b</text>
<rect x="90" y="315" width="45" height="45" style="fill: #a57551" />
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="360" height="360" viewBox="-90 -315 360 360">
<g style="opacity:1; fill:none; fill-rule:evenodd; fill-opacity:1; stroke:#000000; stroke-width:1.5; stroke-linecap:round; stroke-linejoin:round; stroke-miterlimit:4; stroke-dasharray:none; stroke-opacity:1;">
<g style="fill:#ffffff; stroke:#000000; stroke-linecap:butt;">
<path
d="M 9,36 C 12.39,35.03 19.11,36.43 22.5,34 C 25.89,36.43 32.61,35.03 36,36 C 36,36 37.65,36.54 39,38 C 38.32,38.97 37.35,38.99 36,38.5 C 32.61,37.53 25.89,38.96 22.5,37.5 C 19.11,38.96 12.39,37.53 9,38.5 C 7.646,38.99 6.677,38.97 6,38 C 7.354,36.06 9,36 9,36 z" />
<path
d="M 15,32 C 17.5,34.5 27.5,34.5 30,32 C 30.5,30.5 30,30 30,30 C 30,27.5 27.5,26 27.5,26 C 33,24.5 33.5,14.5 22.5,10.5 C 11.5,14.5 12,24.5 17.5,26 C 17.5,26 15,27.5 15,30 C 15,30 14.5,30.5 15,32 z" />
<path
d="M 25 8 A 2.5 2.5 0 1 1 20,8 A 2.5 2.5 0 1 1 25 8 z" />
</g>
<path
d="M 17.5,26 L 27.5,26 M 15,30 L 30,30 M 22.5,15.5 L 22.5,20.5 M 20,18 L 25,18"
style="fill:none; stroke:#000000; stroke-linejoin:miter;" />
</g>
</svg>
<text x="132" y="357" style="text-anchor:end;font-size:11px;fill: #ebd1a6" >c</text>
<rect x="135" y="315" width="45" height="45" style="fill: #ebd1a6" />
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="360" height="360" viewBox="-135 -315 360 360">
<g style="opacity:1; fill:#ffffff; fill-opacity:1; fill-rule:evenodd; stroke:#000000; stroke-width:1.5; stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4; stroke-dasharray:none; stroke-opacity:1;">
<path
d="M 9 13 A 2 2 0 1 1 5,13 A 2 2 0 1 1 9 13 z"
transform="translate(-1,-1)" />
<path
d="M 9 13 A 2 2 0 1 1 5,13 A 2 2 0 1 1 9 13 z"
transform="translate(15.5,-5.5)" />
<path
d="M 9 13 A 2 2 0 1 1 5,13 A 2 2 0 1 1 9 13 z"
transform="translate(32,-1)" />
<path
d="M 9 13 A 2 2 0 1 1 5,13 A 2 2 0 1 1 9 13 z"
transform="translate(7,-4.5)" />
<path
d="M 9 13 A 2 2 0 1 1 5,13 A 2 2 0 1 1 9 13 z"
transform="translate(24,-4)" />
<path
d="M 9,26 C 17.5,24.5 30,24.5 36,26 L 38,14 L 31,25 L 31,11 L 25.5,24.5 L 22.5,9.5 L 19.5,24.5 L 14,10.5 L 14,25 L 7,14 L 9,26 z "
style="stroke-linecap:butt;" />
<path
d="M 9,26 C 9,28 10.5,28 11.5,30 C 12.5,31.5 12.5,31 12,33.5 C 10.5,34.5 10.5,36 10.5,36 C 9,37.5 11,38.5 11,38.5 C 17.5,39.5 27.5,39.5 34,38.5 C 34,38.5 35.5,37.5 34,36 C 34,36 34.5,34.5 33,33.5 C 32.5,31 32.5,31.5 33.5,30 C 34.5,28 36,28 36,26 C 27.5,24.5 17.5,24.5 9,26 z "
style="stroke-linecap:butt;" />
<path
d="M 11.5,30 C 15,29 30,29 33.5,30"
style="fill:none;" />
<path
d="M 12,33.5 C 18,32.5 27,32.5 33,33.5"
style="fill:none;" />
</g>
</svg>
<text x="177" y="357" style="text-anchor:end;font-size:11px;fill: #a57551" >d</text>
<rect x="180" y="315" width="45" height="45" style="fill: #a57551" />
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="360" height="360" viewBox="-180 -315 360 360">
<g style="fill:none; fill-opacity:1; fill-rule:evenodd; stroke:#000000; stroke-width:1.5; stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4; stroke-dasharray:none; stroke-opacity:1;">
<path
d="M 22.5,11.63 L 22.5,6"
style="fill:none; stroke:#000000; stroke-linejoin:miter;" />
<path
d="M 20,8 L 25,8"
style="fill:none; stroke:#000000; stroke-linejoin:miter;" />
<path
d="M 22.5,25 C 22.5,25 27,17.5 25.5,14.5 C 25.5,14.5 24.5,12 22.5,12 C 20.5,12 19.5,14.5 19.5,14.5 C 18,17.5 22.5,25 22.5,25"
style="fill:#ffffff; stroke:#000000; stroke-linecap:butt; stroke-linejoin:miter;" />
<path
d="M 11.5,37 C 17,40.5 27,40.5 32.5,37 L 32.5,30 C 32.5,30 41.5,25.5 38.5,19.5 C 34.5,13 25,16 22.5,23.5 L 22.5,27 L 22.5,23.5 C 19,16 9.5,13 6.5,19.5 C 3.5,25.5 11.5,29.5 11.5,29.5 L 11.5,37 z "
style="fill:#ffffff; stroke:#000000;" />
<path
d="M 11.5,30 C 17,27 27,27 32.5,30"
style="fill:none; stroke:#000000;" />
<path
d="M 11.5,33.5 C 17,30.5 27,30.5 32.5,33.5"
style="fill:none; stroke:#000000;" />
<path
d="M 11.5,37 C 17,34 27,34 32.5,37"
style="fill:none; stroke:#000000;" />
</g>
</svg>
<text x="222" y="357" style="text-anchor:end;font-size:11px;fill: #ebd1a6" >e</text>
<rect x="225" y="315" width="45" height="45" style="fill: #ebd1a6" />
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="360" height="360" viewBox="-225 -315 360 360">
<g style="opacity:1; fill:none; fill-rule:evenodd; fill-opacity:1; stroke:#000000; stroke-width:1.5; stroke-linecap:round; stroke-linejoin:round; stroke-miterlimit:4; stroke-dasharray:none; stroke-opacity:1;">
<g style="fill:#ffffff; stroke:#000000; stroke-linecap:butt;">
<path
d="M 9,36 C 12.39,35.03 19.11,36.43 22.5,34 C 25.89,36.43 32.61,35.03 36,36 C 36,36 37.65,36.54 39,38 C 38.32,38.97 37.35,38.99 36,38.5 C 32.61,37.53 25.89,38.96 22.5,37.5 C 19.11,38.96 12.39,37.53 9,38.5 C 7.646,38.99 6.677,38.97 6,38 C 7.354,36.06 9,36 9,36 z" />
<path
d="M 15,32 C 17.5,34.5 27.5,34.5 30,32 C 30.5,30.5 30,30 30,30 C 30,27.5 27.5,26 27.5,26 C 33,24.5 33.5,14.5 22.5,10.5 C 11.5,14.5 12,24.5 17.5,26 C 17.5,26 15,27.5 15,30 C 15,30 14.5,30.5 15,32 z" />
<path
d="M 25 8 A 2.5 2.5 0 1 1 20,8 A 2.5 2.5 0 1 1 25 8 z" />
</g>
<path
d="M 17.5,26 L 27.5,26 M 15,30 L 30,30 M 22.5,15.5 L 22.5,20.5 M 20,18 L 25,18"
style="fill:none; stroke:#000000; stroke-linejoin:miter;" />
</g>
</svg>
<text x="267" y="357" style="text-anchor:end;font-size:11px;fill: #a57551" >f</text>
<rect x="270" y="315" width="45" height="45" style="fill: #a57551" />
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="360" height="360" viewBox="-270 -315 360 360">
<g style="opacity:1; fill:none; fill-opacity:1; fill-rule:evenodd; stroke:#000000; stroke-width:1.5; stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4; stroke-dasharray:none; stroke-opacity:1;">
<path
d="M 22,10 C 32.5,11 38.5,18 38,39 L 15,39 C 15,30 25,32.5 23,18"
style="fill:#ffffff; stroke:#000000;" />
<path
d="M 24,18 C 24.38,20.91 18.45,25.37 16,27 C 13,29 13.18,31.34 11,31 C 9.958,30.06 12.41,27.96 11,28 C 10,28 11.19,29.23 10,30 C 9,30 5.997,31 6,26 C 6,24 12,14 12,14 C 12,14 13.89,12.1 14,10.5 C 13.27,9.506 13.5,8.5 13.5,7.5 C 14.5,6.5 16.5,10 16.5,10 L 18.5,10 C 18.5,10 19.28,8.008 21,7 C 22,7 22,10 22,10"
style="fill:#ffffff; stroke:#000000;" />
<path
d="M 9.5 25.5 A 0.5 0.5 0 1 1 8.5,25.5 A 0.5 0.5 0 1 1 9.5 25.5 z"
style="fill:#000000; stroke:#000000;" />
<path
d="M 15 15.5 A 0.5 1.5 0 1 1 14,15.5 A 0.5 1.5 0 1 1 15 15.5 z"
transform="matrix(0.866,0.5,-0.5,0.866,9.693,-5.173)"
style="fill:#000000; stroke:#000000;" />
</g>
</svg>
<text x="312" y="357" style="text-anchor:end;font-size:11px;fill: #ebd1a6" >g</text>
<rect x="315" y="315" width="45" height="45" style="fill: #ebd1a6" />
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="360" height="360" viewBox="-315 -315 360 360">
<g style="opacity:1; fill:#ffffff; fill-opacity:1; fill-rule:evenodd; stroke:#000000; stroke-width:1.5; stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4; stroke-dasharray:none; stroke-opacity:1;">
<path
d="M 9,39 L 36,39 L 36,36 L 9,36 L 9,39 z "
style="stroke-linecap:butt;" />
<path
d="M 12,36 L 12,32 L 33,32 L 33,36 L 12,36 z "
style="stroke-linecap:butt;" />
<path
d="M 11,14 L 11,9 L 15,9 L 15,11 L 20,11 L 20,9 L 25,9 L 25,11 L 30,11 L 30,9 L 34,9 L 34,14"
style="stroke-linecap:butt;" />
<path
d="M 34,14 L 31,17 L 14,17 L 11,14" />
<path
d="M 31,17 L 31,29.5 L 14,29.5 L 14,17"
style="stroke-linecap:butt; stroke-linejoin:miter;" />
<path
d="M 31,29.5 L 32.5,32 L 12.5,32 L 14,29.5" />
<path
d="M 11,14 L 34,14"
style="fill:none; stroke:#000000; stroke-linejoin:miter;" />
</g>
</svg>
<text x="357" y="357" style="text-anchor:end;font-size:11px;fill: #a57551" >h</text>
<rect x="0" y="270" width="45" height="45" style="fill: #ebd1a6" />
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="360" height="360" viewBox="0 -270 360 360">
<path
d="M 22,9 C 19.79,9 18,10.79 18,13 C 18,13.89 18.29,14.71 18.78,15.38 C 16.83,16.5 15.5,18.59 15.5,21 C 15.5,23.03 16.44,24.84 17.91,26.03 C 14.91,27.09 10.5,31.58 10.5,39.5 L 33.5,39.5 C 33.5,31.58 29.09,27.09 26.09,26.03 C 27.56,24.84 28.5,23.03 28.5,21 C 28.5,18.59 27.17,16.5 25.22,15.38 C 25.71,14.71 26,13.89 26,13 C 26,10.79 24.21,9 22,9 z "
style="opacity:1; fill:#ffffff; fill-opacity:1; fill-rule:nonzero; stroke:#000000; stroke-width:1.5; stroke-linecap:round; stroke-linejoin:miter; stroke-miterlimit:4; stroke-dasharray:none; stroke-opacity:1;" />
</svg>
<text x="2" y="281" style="font-size:11px;fill: #a57551" >2</text>
<rect x="45" y="270" width="45" height="45" style="fill: #a57551" />
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="360" height="360" viewBox="-45 -270 360 360">
<path
d="M 22,9 C 19.79,9 18,10.79 18,13 C 18,13.89 18.29,14.71 18.78,15.38 C 16.83,16.5 15.5,18.59 15.5,21 C 15.5,23.03 16.44,24.84 17.91,26.03 C 14.91,27.09 10.5,31.58 10.5,39.5 L 33.5,39.5 C 33.5,31.58 29.09,27.09 26.09,26.03 C 27.56,24.84 28.5,23.03 28.5,21 C 28.5,18.59 27.17,16.5 25.22,15.38 C 25.71,14.71 26,13.89 26,13 C 26,10.79 24.21,9 22,9 z "
style="opacity:1; fill:#ffffff; fill-opacity:1; fill-rule:nonzero; stroke:#000000; stroke-width:1.5; stroke-linecap:round; stroke-linejoin:miter; stroke-miterlimit:4; stroke-dasharray:none; stroke-opacity:1;" />
</svg>
<rect x="90" y="270" width="45" height="45" style="fill: #ebd1a6" />
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="360" height="360" viewBox="-90 -270 360 360">
<path
d="M 22,9 C 19.79,9 18,10.79 18,13 C 18,13.89 18.29,14.71 18.78,15.38 C 16.83,16.5 15.5,18.59 15.5,21 C 15.5,23.03 16.44,24.84 17.91,26.03 C 14.91,27.09 10.5,31.58 10.5,39.5 L 33.5,39.5 C 33.5,31.58 29.09,27.09 26.09,26.03 C 27.56,24.84 28.5,23.03 28.5,21 C 28.5,18.59 27.17,16.5 25.22,15.38 C 25.71,14.71 26,13.89 26,13 C 26,10.79 24.21,9 22,9 z "
style="opacity:1; fill:#ffffff; fill-opacity:1; fill-rule:nonzero; stroke:#000000; stroke-width:1.5; stroke-linecap:round; stroke-linejoin:miter; stroke-miterlimit:4; stroke-dasharray:none; stroke-opacity:1;" />
</svg>
<rect x="135" y="270" width="45" height="45" style="fill: #a57551" />
<rect x="135" y="270" width="45" height="45" style="fill-opacity:0.2;fill: #ffff00" />
<rect x="180" y="270" width="45" height="45" style="fill: #ebd1a6" />
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="360" height="360" viewBox="-180 -270 360 360">
<path
d="M 22,9 C 19.79,9 18,10.79 18,13 C 18,13.89 18.29,14.71 18.78,15.38 C 16.83,16.5 15.5,18.59 15.5,21 C 15.5,23.03 16.44,24.84 17.91,26.03 C 14.91,27.09 10.5,31.58 10.5,39.5 L 33.5,39.5 C 33.5,31.58 29.09,27.09 26.09,26.03 C 27.56,24.84 28.5,23.03 28.5,21 C 28.5,18.59 27.17,16.5 25.22,15.38 C 25.71,14.71 26,13.89 26,13 C 26,10.79 24.21,9 22,9 z "
style="opacity:1; fill:#ffffff; fill-opacity:1; fill-rule:nonzero; stroke:#000000; stroke-width:1.5; stroke-linecap:round; stroke-linejoin:miter; stroke-miterlimit:4; stroke-dasharray:none; stroke-opacity:1;" />
</svg>
<rect x="225" y="270" width="45" height="45" style="fill: #a57551" />
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="360" height="360" viewBox="-225 -270 360 360">
<path
d="M 22,9 C 19.79,9 18,10.79 18,13 C 18,13.89 18.29,14.71 18.78,15.38 C 16.83,16.5 15.5,18.59 15.5,21 C 15.5,23.03 16.44,24.84 17.91,26.03 C 14.91,27.09 10.5,31.58 10.5,39.5 L 33.5,39.5 C 33.5,31.58 29.09,27.09 26.09,26.03 C 27.56,24.84 28.5,23.03 28.5,21 C 28.5,18.59 27.17,16.5 25.22,15.38 C 25.71,14.71 26,13.89 26,13 C 26,10.79 24.21,9 22,9 z "
style="opacity:1; fill:#ffffff; fill-opacity:1; fill-rule:nonzero; stroke:#000000; stroke-width:1.5; stroke-linecap:round; stroke-linejoin:miter; stroke-miterlimit:4; stroke-dasharray:none; stroke-opacity:1;" />
</svg>
<rect x="270" y="270" width="45" height="45" style="fill: #ebd1a6" />
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="360" height="360" viewBox="-270 -270 360 360">
<path
d="M 22,9 C 19.79,9 18,10.79 18,13 C 18,13.89 18.29,14.71 18.78,15.38 C 16.83,16.5 15.5,18.59 15.5,21 C 15.5,23.03 16.44,24.84 17.91,26.03 C 14.91,27.09 10.5,31.58 10.5,39.5 L 33.5,39.5 C 33.5,31.58 29.09,27.09 26.09,26.03 C 27.56,24.84 28.5,23.03 28.5,21 C 28.5,18.59 27.17,16.5 25.22,15.38 C 25.71,14.71 26,13.89 26,13 C 26,10.79 24.21,9 22,9 z "
style="opacity:1; fill:#ffffff; fill-opacity:1; fill-rule:nonzero; stroke:#000000; stroke-width:1.5; stroke-linecap:round; stroke-linejoin:miter; stroke-miterlimit:4; stroke-dasharray:none; stroke-opacity:1;" />
</svg>
<rect x="315" y="270" width="45" height="45" style="fill: #a57551" />
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="360" height="360" viewBox="-315 -270 360 360">
<path
d="M 22,9 C 19.79,9 18,10.79 18,13 C 18,13.89 18.29,14.71 18.78,15.38 C 16.83,16.5 15.5,18.59 15.5,21 C 15.5,23.03 16.44,24.84 17.91,26.03 C 14.91,27.09 10.5,31.58 10.5,39.5 L 33.5,39.5 C 33.5,31.58 29.09,27.09 26.09,26.03 C 27.56,24.84 28.5,23.03 28.5,21 C 28.5,18.59 27.17,16.5 25.22,15.38 C 25.71,14.71 26,13.89 26,13 C 26,10.79 24.21,9 22,9 z "
style="opacity:1; fill:#ffffff; fill-opacity:1; fill-rule:nonzero; stroke:#000000; stroke-width:1.5; stroke-linecap:round; stroke-linejoin:miter; stroke-miterlimit:4; stroke-dasharray:none; stroke-opacity:1;" />
</svg>
<rect x="0" y="225" width="45" height="45" style="fill: #a57551" />
<text x="2" y="236" style="font-size:11px;fill: #ebd1a6" >3</text>
<rect x="45" y="225" width="45" height="45" style="fill: #ebd1a6" />
<rect x="90" y="225" width="45" height="45" style="fill: #a57551" />
<rect x="135" y="225" width="45" height="45" style="fill: #ebd1a6" />
<rect x="180" y="225" width="45" height="45" style="fill: #a57551" />
<rect x="225" y="225" width="45" height="45" style="fill: #ebd1a6" />
<rect x="270" y="225" width="45" height="45" style="fill: #a57551" />
<rect x="315" y="225" width="45" height="45" style="fill: #ebd1a6" />
<rect x="0" y="180" width="45" height="45" style="fill: #ebd1a6" />
<text x="2" y="191" style="font-size:11px;fill: #a57551" >4</text>
<rect x="45" y="180" width="45" height="45" style="fill: #a57551" />
<rect x="90" y="180" width="45" height="45" style="fill: #ebd1a6" />
<rect x="135" y="180" width="45" height="45" style="fill: #a57551" />
<rect x="135" y="180" width="45" height="45" style="fill-opacity:0.2;fill: #ffff00" />
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="360" height="360" viewBox="-135 -180 360 360">
<path
d="M 22,9 C 19.79,9 18,10.79 18,13 C 18,13.89 18.29,14.71 18.78,15.38 C 16.83,16.5 15.5,18.59 15.5,21 C 15.5,23.03 16.44,24.84 17.91,26.03 C 14.91,27.09 10.5,31.58 10.5,39.5 L 33.5,39.5 C 33.5,31.58 29.09,27.09 26.09,26.03 C 27.56,24.84 28.5,23.03 28.5,21 C 28.5,18.59 27.17,16.5 25.22,15.38 C 25.71,14.71 26,13.89 26,13 C 26,10.79 24.21,9 22,9 z "
style="opacity:1; fill:#ffffff; fill-opacity:1; fill-rule:nonzero; stroke:#000000; stroke-width:1.5; stroke-linecap:round; stroke-linejoin:miter; stroke-miterlimit:4; stroke-dasharray:none; stroke-opacity:1;" />
</svg>
<rect x="180" y="180" width="45" height="45" style="fill: #ebd1a6" />
<rect x="225" y="180" width="45" height="45" style="fill: #a57551" />
<rect x="270" y="180" width="45" height="45" style="fill: #ebd1a6" />
<rect x="315" y="180" width="45" height="45" style="fill: #a57551" />
<rect x="0" y="135" width="45" height="45" style="fill: #a57551" />
<text x="2" y="146" style="font-size:11px;fill: #ebd1a6" >5</text>
<rect x="45" y="135" width="45" height="45" style="fill: #ebd1a6" />
<rect x="90" y="135" width="45" height="45" style="fill: #a57551" />
<rect x="135" y="135" width="45" height="45" style="fill: #ebd1a6" />
<rect x="180" y="135" width="45" height="45" style="fill: #a57551" />
<rect x="225" y="135" width="45" height="45" style="fill: #ebd1a6" />
<rect x="270" y="135" width="45" height="45" style="fill: #a57551" />
<rect x="315" y="135" width="45" height="45" style="fill: #ebd1a6" />
<rect x="0" y="90" width="45" height="45" style="fill: #ebd1a6" />
<text x="2" y="101" style="font-size:11px;fill: #a57551" >6</text>
<rect x="45" y="90" width="45" height="45" style="fill: #a57551" />
<rect x="90" y="90" width="45" height="45" style="fill: #ebd1a6" />
<rect x="135" y="90" width="45" height="45" style="fill: #a57551" />
<rect x="180" y="90" width="45" height="45" style="fill: #ebd1a6" />
<rect x="225" y="90" width="45" height="45" style="fill: #a57551" />
<rect x="270" y="90" width="45" height="45" style="fill: #ebd1a6" />
<rect x="315" y="90" width="45" height="45" style="fill: #a57551" />
<rect x="0" y="45" width="45" height="45" style="fill: #a57551" />
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="360" height="360" viewBox="0 -45 360 360">
<path
d="M 22,9 C 19.79,9 18,10.79 18,13 C 18,13.89 18.29,14.71 18.78,15.38 C 16.83,16.5 15.5,18.59 15.5,21 C 15.5,23.03 16.44,24.84 17.91,26.03 C 14.91,27.09 10.5,31.58 10.5,39.5 L 33.5,39.5 C 33.5,31.58 29.09,27.09 26.09,26.03 C 27.56,24.84 28.5,23.03 28.5,21 C 28.5,18.59 27.17,16.5 25.22,15.38 C 25.71,14.71 26,13.89 26,13 C 26,10.79 24.21,9 22,9 z "
style="opacity:1; fill:#000000; fill-opacity:1; fill-rule:nonzero; stroke:#000000; stroke-width:1.5; stroke-linecap:round; stroke-linejoin:miter; stroke-miterlimit:4; stroke-dasharray:none; stroke-opacity:1;" />
</svg>
<text x="2" y="56" style="font-size:11px;fill: #ebd1a6" >7</text>
<rect x="45" y="45" width="45" height="45" style="fill: #ebd1a6" />
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="360" height="360" viewBox="-45 -45 360 360">
<path
d="M 22,9 C 19.79,9 18,10.79 18,13 C 18,13.89 18.29,14.71 18.78,15.38 C 16.83,16.5 15.5,18.59 15.5,21 C 15.5,23.03 16.44,24.84 17.91,26.03 C 14.91,27.09 10.5,31.58 10.5,39.5 L 33.5,39.5 C 33.5,31.58 29.09,27.09 26.09,26.03 C 27.56,24.84 28.5,23.03 28.5,21 C 28.5,18.59 27.17,16.5 25.22,15.38 C 25.71,14.71 26,13.89 26,13 C 26,10.79 24.21,9 22,9 z "
style="opacity:1; fill:#000000; fill-opacity:1; fill-rule:nonzero; stroke:#000000; stroke-width:1.5; stroke-linecap:round; stroke-linejoin:miter; stroke-miterlimit:4; stroke-dasharray:none; stroke-opacity:1;" />
</svg>
<rect x="90" y="45" width="45" height="45" style="fill: #a57551" />
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="360" height="360" viewBox="-90 -45 360 360">
<path
d="M 22,9 C 19.79,9 18,10.79 18,13 C 18,13.89 18.29,14.71 18.78,15.38 C 16.83,16.5 15.5,18.59 15.5,21 C 15.5,23.03 16.44,24.84 17.91,26.03 C 14.91,27.09 10.5,31.58 10.5,39.5 L 33.5,39.5 C 33.5,31.58 29.09,27.09 26.09,26.03 C 27.56,24.84 28.5,23.03 28.5,21 C 28.5,18.59 27.17,16.5 25.22,15.38 C 25.71,14.71 26,13.89 26,13 C 26,10.79 24.21,9 22,9 z "
style="opacity:1; fill:#000000; fill-opacity:1; fill-rule:nonzero; stroke:#000000; stroke-width:1.5; stroke-linecap:round; stroke-linejoin:miter; stroke-miterlimit:4; stroke-dasharray:none; stroke-opacity:1;" />
</svg>
<rect x="135" y="45" width="45" height="45" style="fill: #ebd1a6" />
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="360" height="360" viewBox="-135 -45 360 360">
<path
d="M 22,9 C 19.79,9 18,10.79 18,13 C 18,13.89 18.29,14.71 18.78,15.38 C 16.83,16.5 15.5,18.59 15.5,21 C 15.5,23.03 16.44,24.84 17.91,26.03 C 14.91,27.09 10.5,31.58 10.5,39.5 L 33.5,39.5 C 33.5,31.58 29.09,27.09 26.09,26.03 C 27.56,24.84 28.5,23.03 28.5,21 C 28.5,18.59 27.17,16.5 25.22,15.38 C 25.71,14.71 26,13.89 26,13 C 26,10.79 24.21,9 22,9 z "
style="opacity:1; fill:#000000; fill-opacity:1; fill-rule:nonzero; stroke:#000000; stroke-width:1.5; stroke-linecap:round; stroke-linejoin:miter; stroke-miterlimit:4; stroke-dasharray:none; stroke-opacity:1;" />
</svg>
<rect x="180" y="45" width="45" height="45" style="fill: #a57551" />
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="360" height="360" viewBox="-180 -45 360 360">
<path
d="M 22,9 C 19.79,9 18,10.79 18,13 C 18,13.89 18.29,14.71 18.78,15.38 C 16.83,16.5 15.5,18.59 15.5,21 C 15.5,23.03 16.44,24.84 17.91,26.03 C 14.91,27.09 10.5,31.58 10.5,39.5 L 33.5,39.5 C 33.5,31.58 29.09,27.09 26.09,26.03 C 27.56,24.84 28.5,23.03 28.5,21 C 28.5,18.59 27.17,16.5 25.22,15.38 C 25.71,14.71 26,13.89 26,13 C 26,10.79 24.21,9 22,9 z "
style="opacity:1; fill:#000000; fill-opacity:1; fill-rule:nonzero; stroke:#000000; stroke-width:1.5; stroke-linecap:round; stroke-linejoin:miter; stroke-miterlimit:4; stroke-dasharray:none; stroke-opacity:1;" />
</svg>
<rect x="225" y="45" width="45" height="45" style="fill: #ebd1a6" />
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="360" height="360" viewBox="-225 -45 360 360">
<path
d="M 22,9 C 19.79,9 18,10.79 18,13 C 18,13.89 18.29,14.71 18.78,15.38 C 16.83,16.5 15.5,18.59 15.5,21 C 15.5,23.03 16.44,24.84 17.91,26.03 C 14.91,27.09 10.5,31.58 10.5,39.5 L 33.5,39.5 C 33.5,31.58 29.09,27.09 26.09,26.03 C 27.56,24.84 28.5,23.03 28.5,21 C 28.5,18.59 27.17,16.5 25.22,15.38 C 25.71,14.71 26,13.89 26,13 C 26,10.79 24.21,9 22,9 z "
style="opacity:1; fill:#000000; fill-opacity:1; fill-rule:nonzero; stroke:#000000; stroke-width:1.5; stroke-linecap:round; stroke-linejoin:miter; stroke-miterlimit:4; stroke-dasharray:none; stroke-opacity:1;" />
</svg>
<rect x="270" y="45" width="45" height="45" style="fill: #a57551" />
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="360" height="360" viewBox="-270 -45 360 360">
<path
d="M 22,9 C 19.79,9 18,10.79 18,13 C 18,13.89 18.29,14.71 18.78,15.38 C 16.83,16.5 15.5,18.59 15.5,21 C 15.5,23.03 16.44,24.84 17.91,26.03 C 14.91,27.09 10.5,31.58 10.5,39.5 L 33.5,39.5 C 33.5,31.58 29.09,27.09 26.09,26.03 C 27.56,24.84 28.5,23.03 28.5,21 C 28.5,18.59 27.17,16.5 25.22,15.38 C 25.71,14.71 26,13.89 26,13 C 26,10.79 24.21,9 22,9 z "
style="opacity:1; fill:#000000; fill-opacity:1; fill-rule:nonzero; stroke:#000000; stroke-width:1.5; stroke-linecap:round; stroke-linejoin:miter; stroke-miterlimit:4; stroke-dasharray:none; stroke-opacity:1;" />
</svg>
<rect x="315" y="45" width="45" height="45" style="fill: #ebd1a6" />
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="360" height="360" viewBox="-315 -45 360 360">
<path
d="M 22,9 C 19.79,9 18,10.79 18,13 C 18,13.89 18.29,14.71 18.78,15.38 C 16.83,16.5 15.5,18.59 15.5,21 C 15.5,23.03 16.44,24.84 17.91,26.03 C 14.91,27.09 10.5,31.58 10.5,39.5 L 33.5,39.5 C 33.5,31.58 29.09,27.09 26.09,26.03 C 27.56,24.84 28.5,23.03 28.5,21 C 28.5,18.59 27.17,16.5 25.22,15.38 C 25.71,14.71 26,13.89 26,13 C 26,10.79 24.21,9 22,9 z "
style="opacity:1; fill:#000000; fill-opacity:1; fill-rule:nonzero; stroke:#000000; stroke-width:1.5; stroke-linecap:round; stroke-linejoin:miter; stroke-miterlimit:4; stroke-dasharray:none; stroke-opacity:1;" />
</svg>
<rect x="0" y="0" width="45" height="45" style="fill: #ebd1a6" />
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="360" height="360" viewBox="0 0 360 360">
<g style="opacity:1; fill:000000; fill-opacity:1; fill-rule:evenodd; stroke:#000000; stroke-width:1.5; stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4; stroke-dasharray:none; stroke-opacity:1;">
<path
d="M 9,39 L 36,39 L 36,36 L 9,36 L 9,39 z "
style="stroke-linecap:butt;" />
<path
d="M 12.5,32 L 14,29.5 L 31,29.5 L 32.5,32 L 12.5,32 z "
style="stroke-linecap:butt;" />
<path
d="M 12,36 L 12,32 L 33,32 L 33,36 L 12,36 z "
style="stroke-linecap:butt;" />
<path
d="M 14,29.5 L 14,16.5 L 31,16.5 L 31,29.5 L 14,29.5 z "
style="stroke-linecap:butt;stroke-linejoin:miter;" />
<path
d="M 14,16.5 L 11,14 L 34,14 L 31,16.5 L 14,16.5 z "
style="stroke-linecap:butt;" />
<path
d="M 11,14 L 11,9 L 15,9 L 15,11 L 20,11 L 20,9 L 25,9 L 25,11 L 30,11 L 30,9 L 34,9 L 34,14 L 11,14 z "
style="stroke-linecap:butt;" />
<path
d="M 12,35.5 L 33,35.5 L 33,35.5"
style="fill:none; stroke:#ffffff; stroke-width:1; stroke-linejoin:miter;" />
<path
d="M 13,31.5 L 32,31.5"
style="fill:none; stroke:#ffffff; stroke-width:1; stroke-linejoin:miter;" />
<path
d="M 14,29.5 L 31,29.5"
style="fill:none; stroke:#ffffff; stroke-width:1; stroke-linejoin:miter;" />
<path
d="M 14,16.5 L 31,16.5"
style="fill:none; stroke:#ffffff; stroke-width:1; stroke-linejoin:miter;" />
<path
d="M 11,14 L 34,14"
style="fill:none; stroke:#ffffff; stroke-width:1; stroke-linejoin:miter;" />
</g>
</svg>
<text x="2" y="11" style="font-size:11px;fill: #a57551" >8</text>
<rect x="45" y="0" width="45" height="45" style="fill: #a57551" />
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="360" height="360" viewBox="-45 0 360 360">
<g style="opacity:1; fill:none; fill-opacity:1; fill-rule:evenodd; stroke:#000000; stroke-width:1.5; stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4; stroke-dasharray:none; stroke-opacity:1;">
<path
d="M 22,10 C 32.5,11 38.5,18 38,39 L 15,39 C 15,30 25,32.5 23,18"
style="fill:#000000; stroke:#000000;" />
<path
d="M 24,18 C 24.38,20.91 18.45,25.37 16,27 C 13,29 13.18,31.34 11,31 C 9.958,30.06 12.41,27.96 11,28 C 10,28 11.19,29.23 10,30 C 9,30 5.997,31 6,26 C 6,24 12,14 12,14 C 12,14 13.89,12.1 14,10.5 C 13.27,9.506 13.5,8.5 13.5,7.5 C 14.5,6.5 16.5,10 16.5,10 L 18.5,10 C 18.5,10 19.28,8.008 21,7 C 22,7 22,10 22,10"
style="fill:#000000; stroke:#000000;" />
<path
d="M 9.5 25.5 A 0.5 0.5 0 1 1 8.5,25.5 A 0.5 0.5 0 1 1 9.5 25.5 z"
style="fill:#ffffff; stroke:#ffffff;" />
<path
d="M 15 15.5 A 0.5 1.5 0 1 1 14,15.5 A 0.5 1.5 0 1 1 15 15.5 z"
transform="matrix(0.866,0.5,-0.5,0.866,9.693,-5.173)"
style="fill:#ffffff; stroke:#ffffff;" />
<path
d="M 24.55,10.4 L 24.1,11.85 L 24.6,12 C 27.75,13 30.25,14.49 32.5,18.75 C 34.75,23.01 35.75,29.06 35.25,39 L 35.2,39.5 L 37.45,39.5 L 37.5,39 C 38,28.94 36.62,22.15 34.25,17.66 C 31.88,13.17 28.46,11.02 25.06,10.5 L 24.55,10.4 z "
style="fill:#ffffff; stroke:none;" />
</g>
</svg>
<rect x="90" y="0" width="45" height="45" style="fill: #ebd1a6" />
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="360" height="360" viewBox="-90 0 360 360">
<g style="opacity:1; fill:none; fill-rule:evenodd; fill-opacity:1; stroke:#000000; stroke-width:1.5; stroke-linecap:round; stroke-linejoin:round; stroke-miterlimit:4; stroke-dasharray:none; stroke-opacity:1;">
<g style="fill:#000000; stroke:#000000; stroke-linecap:butt;">
<path
d="M 9,36 C 12.39,35.03 19.11,36.43 22.5,34 C 25.89,36.43 32.61,35.03 36,36 C 36,36 37.65,36.54 39,38 C 38.32,38.97 37.35,38.99 36,38.5 C 32.61,37.53 25.89,38.96 22.5,37.5 C 19.11,38.96 12.39,37.53 9,38.5 C 7.646,38.99 6.677,38.97 6,38 C 7.354,36.06 9,36 9,36 z" />
<path
d="M 15,32 C 17.5,34.5 27.5,34.5 30,32 C 30.5,30.5 30,30 30,30 C 30,27.5 27.5,26 27.5,26 C 33,24.5 33.5,14.5 22.5,10.5 C 11.5,14.5 12,24.5 17.5,26 C 17.5,26 15,27.5 15,30 C 15,30 14.5,30.5 15,32 z" />
<path
d="M 25 8 A 2.5 2.5 0 1 1 20,8 A 2.5 2.5 0 1 1 25 8 z" />
</g>
<path
d="M 17.5,26 L 27.5,26 M 15,30 L 30,30 M 22.5,15.5 L 22.5,20.5 M 20,18 L 25,18"
style="fill:none; stroke:#ffffff; stroke-linejoin:miter;" />
</g>
</svg>
<rect x="135" y="0" width="45" height="45" style="fill: #a57551" />
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="360" height="360" viewBox="-135 0 360 360">
<g style="opacity:1; fill:000000; fill-opacity:1; fill-rule:evenodd; stroke:#000000; stroke-width:1.5; stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4; stroke-dasharray:none; stroke-opacity:1;">
<g style="fill:#000000; stroke:none;">
<circle cx="6" cy="12" r="2.75" />
<circle cx="14" cy="9" r="2.75" />
<circle cx="22.5" cy="8" r="2.75" />
<circle cx="31" cy="9" r="2.75" />
<circle cx="39" cy="12" r="2.75" />
</g>
<path
d="M 9,26 C 17.5,24.5 30,24.5 36,26 L 38.5,13.5 L 31,25 L 30.7,10.9 L 25.5,24.5 L 22.5,10 L 19.5,24.5 L 14.3,10.9 L 14,25 L 6.5,13.5 L 9,26 z"
style="stroke-linecap:butt; stroke:#000000;" />
<path
d="M 9,26 C 9,28 10.5,28 11.5,30 C 12.5,31.5 12.5,31 12,33.5 C 10.5,34.5 10.5,36 10.5,36 C 9,37.5 11,38.5 11,38.5 C 17.5,39.5 27.5,39.5 34,38.5 C 34,38.5 35.5,37.5 34,36 C 34,36 34.5,34.5 33,33.5 C 32.5,31 32.5,31.5 33.5,30 C 34.5,28 36,28 36,26 C 27.5,24.5 17.5,24.5 9,26 z"
style="stroke-linecap:butt;" />
<path
d="M 11,38.5 A 35,35 1 0 0 34,38.5"
style="fill:none; stroke:#000000; stroke-linecap:butt;" />
<path
d="M 11,29 A 35,35 1 0 1 34,29"
style="fill:none; stroke:#ffffff;" />
<path
d="M 12.5,31.5 L 32.5,31.5"
style="fill:none; stroke:#ffffff;" />
<path
d="M 11.5,34.5 A 35,35 1 0 0 33.5,34.5"
style="fill:none; stroke:#ffffff;" />
<path
d="M 10.5,37.5 A 35,35 1 0 0 34.5,37.5"
style="fill:none; stroke:#ffffff;" />
</g>
</svg>
<rect x="180" y="0" width="45" height="45" style="fill: #ebd1a6" />
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="360" height="360" viewBox="-180 0 360 360">
<g style="fill:none; fill-opacity:1; fill-rule:evenodd; stroke:#000000; stroke-width:1.5; stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4; stroke-dasharray:none; stroke-opacity:1;">
<path
d="M 22.5,11.63 L 22.5,6"
style="fill:none; stroke:#000000; stroke-linejoin:miter;"
id="path6570" />
<path
d="M 22.5,25 C 22.5,25 27,17.5 25.5,14.5 C 25.5,14.5 24.5,12 22.5,12 C 20.5,12 19.5,14.5 19.5,14.5 C 18,17.5 22.5,25 22.5,25"
style="fill:#000000;fill-opacity:1; stroke-linecap:butt; stroke-linejoin:miter;" />
<path
d="M 11.5,37 C 17,40.5 27,40.5 32.5,37 L 32.5,30 C 32.5,30 41.5,25.5 38.5,19.5 C 34.5,13 25,16 22.5,23.5 L 22.5,27 L 22.5,23.5 C 19,16 9.5,13 6.5,19.5 C 3.5,25.5 11.5,29.5 11.5,29.5 L 11.5,37 z "
style="fill:#000000; stroke:#000000;" />
<path
d="M 20,8 L 25,8"
style="fill:none; stroke:#000000; stroke-linejoin:miter;" />
<path
d="M 32,29.5 C 32,29.5 40.5,25.5 38.03,19.85 C 34.15,14 25,18 22.5,24.5 L 22.51,26.6 L 22.5,24.5 C 20,18 9.906,14 6.997,19.85 C 4.5,25.5 11.85,28.85 11.85,28.85"
style="fill:none; stroke:#ffffff;" />
<path
d="M 11.5,30 C 17,27 27,27 32.5,30 M 11.5,33.5 C 17,30.5 27,30.5 32.5,33.5 M 11.5,37 C 17,34 27,34 32.5,37"
style="fill:none; stroke:#ffffff;" />
</g>
</svg>
<rect x="225" y="0" width="45" height="45" style="fill: #a57551" />
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="360" height="360" viewBox="-225 0 360 360">
<g style="opacity:1; fill:none; fill-rule:evenodd; fill-opacity:1; stroke:#000000; stroke-width:1.5; stroke-linecap:round; stroke-linejoin:round; stroke-miterlimit:4; stroke-dasharray:none; stroke-opacity:1;">
<g style="fill:#000000; stroke:#000000; stroke-linecap:butt;">
<path
d="M 9,36 C 12.39,35.03 19.11,36.43 22.5,34 C 25.89,36.43 32.61,35.03 36,36 C 36,36 37.65,36.54 39,38 C 38.32,38.97 37.35,38.99 36,38.5 C 32.61,37.53 25.89,38.96 22.5,37.5 C 19.11,38.96 12.39,37.53 9,38.5 C 7.646,38.99 6.677,38.97 6,38 C 7.354,36.06 9,36 9,36 z" />
<path
d="M 15,32 C 17.5,34.5 27.5,34.5 30,32 C 30.5,30.5 30,30 30,30 C 30,27.5 27.5,26 27.5,26 C 33,24.5 33.5,14.5 22.5,10.5 C 11.5,14.5 12,24.5 17.5,26 C 17.5,26 15,27.5 15,30 C 15,30 14.5,30.5 15,32 z" />
<path
d="M 25 8 A 2.5 2.5 0 1 1 20,8 A 2.5 2.5 0 1 1 25 8 z" />
</g>
<path
d="M 17.5,26 L 27.5,26 M 15,30 L 30,30 M 22.5,15.5 L 22.5,20.5 M 20,18 L 25,18"
style="fill:none; stroke:#ffffff; stroke-linejoin:miter;" />
</g>
</svg>
<rect x="270" y="0" width="45" height="45" style="fill: #ebd1a6" />
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="360" height="360" viewBox="-270 0 360 360">
<g style="opacity:1; fill:none; fill-opacity:1; fill-rule:evenodd; stroke:#000000; stroke-width:1.5; stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4; stroke-dasharray:none; stroke-opacity:1;">
<path
d="M 22,10 C 32.5,11 38.5,18 38,39 L 15,39 C 15,30 25,32.5 23,18"
style="fill:#000000; stroke:#000000;" />
<path
d="M 24,18 C 24.38,20.91 18.45,25.37 16,27 C 13,29 13.18,31.34 11,31 C 9.958,30.06 12.41,27.96 11,28 C 10,28 11.19,29.23 10,30 C 9,30 5.997,31 6,26 C 6,24 12,14 12,14 C 12,14 13.89,12.1 14,10.5 C 13.27,9.506 13.5,8.5 13.5,7.5 C 14.5,6.5 16.5,10 16.5,10 L 18.5,10 C 18.5,10 19.28,8.008 21,7 C 22,7 22,10 22,10"
style="fill:#000000; stroke:#000000;" />
<path
d="M 9.5 25.5 A 0.5 0.5 0 1 1 8.5,25.5 A 0.5 0.5 0 1 1 9.5 25.5 z"
style="fill:#ffffff; stroke:#ffffff;" />
<path
d="M 15 15.5 A 0.5 1.5 0 1 1 14,15.5 A 0.5 1.5 0 1 1 15 15.5 z"
transform="matrix(0.866,0.5,-0.5,0.866,9.693,-5.173)"
style="fill:#ffffff; stroke:#ffffff;" />
<path
d="M 24.55,10.4 L 24.1,11.85 L 24.6,12 C 27.75,13 30.25,14.49 32.5,18.75 C 34.75,23.01 35.75,29.06 35.25,39 L 35.2,39.5 L 37.45,39.5 L 37.5,39 C 38,28.94 36.62,22.15 34.25,17.66 C 31.88,13.17 28.46,11.02 25.06,10.5 L 24.55,10.4 z "
style="fill:#ffffff; stroke:none;" />
</g>
</svg>
<rect x="315" y="0" width="45" height="45" style="fill: #a57551" />
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="360" height="360" viewBox="-315 0 360 360">
<g style="opacity:1; fill:000000; fill-opacity:1; fill-rule:evenodd; stroke:#000000; stroke-width:1.5; stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4; stroke-dasharray:none; stroke-opacity:1;">
<path
d="M 9,39 L 36,39 L 36,36 L 9,36 L 9,39 z "
style="stroke-linecap:butt;" />
<path
d="M 12.5,32 L 14,29.5 L 31,29.5 L 32.5,32 L 12.5,32 z "
style="stroke-linecap:butt;" />
<path
d="M 12,36 L 12,32 L 33,32 L 33,36 L 12,36 z "
style="stroke-linecap:butt;" />
<path
d="M 14,29.5 L 14,16.5 L 31,16.5 L 31,29.5 L 14,29.5 z "
style="stroke-linecap:butt;stroke-linejoin:miter;" />
<path
d="M 14,16.5 L 11,14 L 34,14 L 31,16.5 L 14,16.5 z "
style="stroke-linecap:butt;" />
<path
d="M 11,14 L 11,9 L 15,9 L 15,11 L 20,11 L 20,9 L 25,9 L 25,11 L 30,11 L 30,9 L 34,9 L 34,14 L 11,14 z "
style="stroke-linecap:butt;" />
<path
d="M 12,35.5 L 33,35.5 L 33,35.5"
style="fill:none; stroke:#ffffff; stroke-width:1; stroke-linejoin:miter;" />
<path
d="M 13,31.5 L 32,31.5"
style="fill:none; stroke:#ffffff; stroke-width:1; stroke-linejoin:miter;" />
<path
d="M 14,29.5 L 31,29.5"
style="fill:none; stroke:#ffffff; stroke-width:1; stroke-linejoin:miter;" />
<path
d="M 14,16.5 L 31,16.5"
style="fill:none; stroke:#ffffff; stroke-width:1; stroke-linejoin:miter;" />
<path
d="M 11,14 L 34,14"
style="fill:none; stroke:#ffffff; stroke-width:1; stroke-linejoin:miter;" />
</g>
</svg>
</svg>

After

Width:  |  Height:  |  Size: 37 KiB

168
image/image.go

@ -0,0 +1,168 @@ @@ -0,0 +1,168 @@
// Package image is a go library that creates images from board positions
package image
import (
"fmt"
"image/color"
"io"
"strings"
svg "github.com/ajstarks/svgo"
"git.nightmare.haus/rudi/chessv2"
"git.nightmare.haus/rudi/chessv2/image/internal"
)
// SVG writes the board SVG representation into the writer.
// An error is returned if there is there is an error writing data.
// SVG also takes options which can customize the image output.
func SVG(w io.Writer, b *chess.Board, opts ...func(*encoder)) error {
e := new(w, opts)
return e.EncodeSVG(b)
}
// SquareColors is designed to be used as an optional argument
// to the SVG function. It changes the default light and
// dark square colors to the colors given.
func SquareColors(light, dark color.Color) func(*encoder) {
return func(e *encoder) {
e.light = light
e.dark = dark
}
}
// MarkSquares is designed to be used as an optional argument
// to the SVG function. It marks the given squares with the
// color. A possible usage includes marking squares of the
// previous move.
func MarkSquares(c color.Color, sqs ...chess.Square) func(*encoder) {
return func(e *encoder) {
for _, sq := range sqs {
e.marks[sq] = c
}
}
}
// A Encoder encodes chess boards into images.
type encoder struct {
w io.Writer
light color.Color
dark color.Color
marks map[chess.Square]color.Color
}
// New returns an encoder that writes to the given writer.
// New also takes options which can customize the image
// output.
func new(w io.Writer, options []func(*encoder)) *encoder {
e := &encoder{
w: w,
light: color.RGBA{235, 209, 166, 1},
dark: color.RGBA{165, 117, 81, 1},
marks: map[chess.Square]color.Color{},
}
for _, op := range options {
op(e)
}
return e
}
const (
sqWidth = 45
sqHeight = 45
boardWidth = 8 * sqWidth
boardHeight = 8 * sqHeight
)
var (
orderOfRanks = []chess.Rank{chess.Rank8, chess.Rank7, chess.Rank6, chess.Rank5, chess.Rank4, chess.Rank3, chess.Rank2, chess.Rank1}
orderOfFiles = []chess.File{chess.FileA, chess.FileB, chess.FileC, chess.FileD, chess.FileE, chess.FileF, chess.FileG, chess.FileH}
)
// EncodeSVG writes the board SVG representation into
// the Encoder's writer. An error is returned if there
// is there is an error writing data.
func (e *encoder) EncodeSVG(b *chess.Board) error {
boardMap := b.SquareMap()
canvas := svg.New(e.w)
canvas.Start(boardWidth, boardHeight)
canvas.Rect(0, 0, boardWidth, boardHeight)
for i := 0; i < 64; i++ {
sq := chess.Square(i)
x, y := xyForSquare(sq)
// draw square
c := e.colorForSquare(sq)
canvas.Rect(x, y, sqWidth, sqHeight, "fill: "+colorToHex(c))
markColor, ok := e.marks[sq]
if ok {
canvas.Rect(x, y, sqWidth, sqHeight, "fill-opacity:0.2;fill: "+colorToHex(markColor))
}
// draw piece
p := boardMap[sq]
if p != chess.NoPiece {
xml := pieceXML(x, y, p)
if _, err := io.WriteString(canvas.Writer, xml); err != nil {
return err
}
}
// draw rank text on file A
txtColor := e.colorForText(sq)
if sq.File() == chess.FileA {
style := "font-size:11px;fill: " + colorToHex(txtColor)
canvas.Text(x+(sqWidth*1/20), y+(sqHeight*5/20), sq.Rank().String(), style)
}
// draw file text on rank 1
if sq.Rank() == chess.Rank1 {
style := "text-anchor:end;font-size:11px;fill: " + colorToHex(txtColor)
canvas.Text(x+(sqWidth*19/20), y+sqHeight-(sqHeight*1/15), sq.File().String(), style)
}
}
canvas.End()
return nil
}
func (e *encoder) colorForSquare(sq chess.Square) color.Color {
sqSum := int(sq.File()) + int(sq.Rank())
if sqSum%2 == 0 {
return e.dark
}
return e.light
}
func (e *encoder) colorForText(sq chess.Square) color.Color {
sqSum := int(sq.File()) + int(sq.Rank())
if sqSum%2 == 0 {
return e.light
}
return e.dark
}
func xyForSquare(sq chess.Square) (x, y int) {
fileIndex := int(sq.File())
rankIndex := 7 - int(sq.Rank())
return fileIndex * sqWidth, rankIndex * sqHeight
}
func colorToHex(c color.Color) string {
r, g, b, _ := c.RGBA()
return fmt.Sprintf("#%02x%02x%02x", uint8(float64(r)+0.5), uint8(float64(g)*1.0+0.5), uint8(float64(b)*1.0+0.5))
}
func pieceXML(x, y int, p chess.Piece) string {
fileName := fmt.Sprintf("pieces/%s%s.svg", p.Color().String(), pieceTypeMap[p.Type()])
svgStr := string(internal.MustAsset(fileName))
old := `<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="45" height="45">`
new := fmt.Sprintf(`<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="360" height="360" viewBox="%d %d 360 360">`, (-1 * x), (-1 * y))
return strings.Replace(svgStr, old, new, 1)
}
var (
pieceTypeMap = map[chess.PieceType]string{
chess.King: "K",
chess.Queen: "Q",
chess.Rook: "R",
chess.Bishop: "B",
chess.Knight: "N",
chess.Pawn: "P",
}
)

48
image/image_test.go

@ -0,0 +1,48 @@ @@ -0,0 +1,48 @@
package image_test
import (
"bytes"
"crypto/md5"
"fmt"
"image/color"
"io"
"os"
"strings"
"testing"
"git.nightmare.haus/rudi/chessv2"
"git.nightmare.haus/rudi/chessv2/image"
)
const expectedMD5 = "da140af8b83ce7903915ee39973e36dd"
func TestSVG(t *testing.T) {
// create buffer of actual svg
buf := bytes.NewBuffer([]byte{})
fenStr := "rnbqkbnr/pppppppp/8/8/3P4/8/PPP1PPPP/RNBQKBNR b KQkq - 0 1"
pos := &chess.Position{}
if err := pos.UnmarshalText([]byte(fenStr)); err != nil {
t.Error(err)
}
mark := image.MarkSquares(color.RGBA{255, 255, 0, 1}, chess.D2, chess.D4)
if err := image.SVG(buf, pos.Board(), mark); err != nil {
t.Error(err)
}
// compare to expected svg
actualSVG := strings.TrimSpace(buf.String())
actualMD5 := fmt.Sprintf("%x", md5.Sum([]byte(actualSVG)))
if actualMD5 != expectedMD5 {
t.Errorf("expected actual md5 hash to be %s but got %s", expectedMD5, actualMD5)
}
// create actual svg file for visualization
f, err := os.Create("example.svg")
defer f.Close()
if err != nil {
t.Error(err)
}
if _, err := io.Copy(f, bytes.NewBufferString(actualSVG)); err != nil {
t.Error(err)
}
}

513
image/internal/bindata.go

@ -0,0 +1,513 @@ @@ -0,0 +1,513 @@
// Code generated by go-bindata.
// sources:
// pieces/.DS_Store
// pieces/bB.svg
// pieces/bK.svg
// pieces/bN.svg
// pieces/bP.svg
// pieces/bQ.svg
// pieces/bR.svg
// pieces/wB.svg
// pieces/wK.svg
// pieces/wN.svg
// pieces/wP.svg
// pieces/wQ.svg
// pieces/wR.svg
// DO NOT EDIT!
package internal
import (
"bytes"
"compress/gzip"
"fmt"
"io"
"io/ioutil"
"os"
"path/filepath"
"strings"
"time"
)
func bindataRead(data []byte, name string) ([]byte, error) {
gz, err := gzip.NewReader(bytes.NewBuffer(data))
if err != nil {
return nil, fmt.Errorf("Read %q: %v", name, err)
}
var buf bytes.Buffer
_, err = io.Copy(&buf, gz)
clErr := gz.Close()
if err != nil {
return nil, fmt.Errorf("Read %q: %v", name, err)
}
if clErr != nil {
return nil, err
}
return buf.Bytes(), nil
}
type asset struct {
bytes []byte
info os.FileInfo
}
type bindataFileInfo struct {
name string
size int64
mode os.FileMode
modTime time.Time
}
func (fi bindataFileInfo) Name() string {
return fi.name
}
func (fi bindataFileInfo) Size() int64 {
return fi.size
}
func (fi bindataFileInfo) Mode() os.FileMode {
return fi.mode
}
func (fi bindataFileInfo) ModTime() time.Time {
return fi.modTime
}
func (fi bindataFileInfo) IsDir() bool {
return false
}
func (fi bindataFileInfo) Sys() interface{} {
return nil
}
var _piecesDs_store = []byte("\x1f\x8b\x08\x00\x00\x09\x6e\x88\x00\xff\xec\x98\x3b\x0e\xc2\x30\x10\x44\x77\x8c\x0b\x4b\x34\x2e\x29\xdd\x70\x00\x6e\x60\x45\xe1\x04\x5c\x80\x82\x2b\xd0\xfb\xe8\x24\xda\x11\xb2\x14\x52\x50\x25\x82\x79\x92\xf5\x56\x8a\x9d\x4f\xe3\xec\xd8\xcc\x30\x3c\x1f\x17\xb3\x3c\x95\xc9\xdc\x76\xb6\x8f\x24\x8e\x05\xa1\xab\xc1\x7b\x08\x21\x84\x10\x62\xdf\xc0\x95\x8e\xdb\xbe\x86\x10\x62\x87\xcc\xfb\x43\xa1\x2b\xdd\xdc\xe0\xf5\x40\xc7\x6e\x4d\xa6\x0b\x5d\xe9\xe6\x06\xe7\x05\x3a\xd2\x89\xce\x74\xa1\x2b\xdd\xdc\xdc\xb4\xc0\xf0\x01\x3e\x19\x4c\x28\x60\x0a\x41\xa1\xeb\x97\x1f\x2d\xc4\x9f\x70\x70\xe5\xf9\xff\x7f\xb5\xd5\xfc\x2f\x84\xf8\x61\x10\xc7\xdb\x38\xd8\x3b\x10\x2c\x27\x4c\xe3\xde\xd5\xcd\xd6\x9b\x80\xe0\x87\x85\xa7\x6e\x6d\xa1\x2b\xdd\xdc\x6a\x04\x84\xd8\x8a\x57\x00\x00\x00\xff\xff\x6a\x00\x88\x6d\x04\x18\x00\x00")
func piecesDs_storeBytes() ([]byte, error) {
return bindataRead(
_piecesDs_store,
"pieces/.DS_Store",
)
}
func piecesDs_store() (*asset, error) {
bytes, err := piecesDs_storeBytes()
if err != nil {
return nil, err
}
info := bindataFileInfo{name: "pieces/.DS_Store", size: 6148, mode: os.FileMode(420), modTime: time.Unix(1445781173, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
var _piecesBbSvg = []byte("\x1f\x8b\x08\x00\x00\x09\x6e\x88\x00\xff\x84\x54\xed\x72\x9b\x30\x10\xfc\x9f\xa7\xb8\x51\xfe\x3a\x42\x1f\x48\x7c\xc4\xce\x4c\xa7\x7f\xe3\x87\xa0\x41\x31\xb4\x04\x3c\x20\x9b\xba\x4f\x5f\x9d\x24\x08\x4d\x3a\x2d\x99\xa0\xf3\x6a\x6f\x77\x4f\x63\x79\x3f\x5d\x4f\xf0\xf3\xad\xeb\xa7\x03\x69\xac\x3d\x97\x49\x32\xcf\x33\x9d\x25\x1d\xc6\x53\x22\x18\x63\x89\x63\x10\xb8\x9a\x71\x6a\x87\xfe\x40\x38\xe5\x04\xe6\xb6\xb6\xcd\x81\xa4\x8a\x40\x63\xda\x53\x63\x7d\xfd\x74\x07\xb0\x3f\xc1\x64\x6f\x9d\x39\x90\xe1\x5c\xbd\xb4\xf6\x56\xf2\x47\x78\x6d\xbb\xae\xec\x87\xde\x84\xf2\x61\xbc\x74\xa6\x34\x57\xd3\x0f\x75\x1d\xa1\x0d\x7b\xb2\xe3\xf0\xc3\x94\xf7\xcc\x3f\xcb\xe7\x07\xef\x59\x72\xaa\x56\xa4\x6b\x7b\xf3\x52\x9d\xcb\x71\xb8\xf4\xf5\x1f\xe8\xf7\xa1\xed\x3f\xc0\x6f\xad\x35\x63\xd7\xba\xa5\x4c\x57\xb0\xae\xa6\xa6\x1a\xc7\xea\x16\xd3\x45\xf8\x3d\x8c\x9f\x69\x3b\x95\x1f\xe5\x43\xb4\x4f\x51\x97\x60\xdf\x2e\xd6\x2e\x12\x4e\xe4\x5c\xd9\x26\xd6\x00\xf5\x81\x1c\xa1\xd8\x49\x0d\x5f\x81\x0b\x2a\x5d\xa9\x28\x93\xc0\x0b\xca\xb9\x83\x69\x2a\x41\x08\xaa\x76\x32\x75\x0c\xa1\x68\x5e\x44\x54\x0a\xaa\x79\x64\x4b\x1d\x14\xc2\x2a\x33\xaa\x15\xb2\x54\x0a\x28\x98\xe3\x4e\x4e\xa5\x70\x25\x2d\x32\xdc\x97\xca\xd7\x85\xef\xc8\xa9\x42\x46\xd0\xcb\xa8\x92\x8b\x8f\x63\xe8\xe8\x9e\x79\x4e\x4c\xe5\xf1\x98\xd6\xf3\x8b\x45\xc4\x39\xa7\x3a\x4a\x6b\xaa\xb3\x2c\x5a\xea\x90\x02\x8d\x53\x4c\xc6\x74\x18\xda\xbf\x7e\x11\x48\xfe\x71\x3a\xdc\xd9\x0b\x34\xcf\xfc\x31\x38\x1f\xb1\x56\x92\x85\x3d\xc9\x10\x61\x11\x61\xf1\x8d\xf8\x0e\xc9\xa1\x43\xe8\x75\x75\x3b\x72\x27\xbc\x82\x74\x08\xf7\xaa\x38\x28\x67\x61\x50\xbe\xa0\x5c\x04\x1e\x5f\x3b\x97\xca\xe5\xf2\xda\x98\x0f\xbd\xc2\x8a\x4d\x21\x49\xc8\xfd\x9f\xe1\x84\x82\x1c\xbe\x80\x40\x7f\xf7\xef\xfa\xdd\x1f\x08\xb6\xfb\x1b\x8a\xdc\x77\xbd\x7d\x72\x8a\xc5\x56\x36\x1c\x59\x8c\xf8\xbc\x0e\x7c\x8c\xf1\x9e\xe3\xd1\x1c\xe3\xb8\xca\xc9\x3f\x87\x5a\x60\xe8\x23\x7a\xf3\x1c\x31\xb7\x9b\x93\x45\x76\xfb\xbd\xdf\x5e\x92\xf2\xfe\xd5\x3f\x9f\xef\x9d\xbf\x69\x8f\x31\xad\xcf\xba\xc7\xdf\x90\xa7\xbb\xdf\x01\x00\x00\xff\xff\x88\x7d\xb5\x19\x6c\x04\x00\x00")
func piecesBbSvgBytes() ([]byte, error) {
return bindataRead(
_piecesBbSvg,
"pieces/bB.svg",
)
}
func piecesBbSvg() (*asset, error) {
bytes, err := piecesBbSvgBytes()
if err != nil {
return nil, err
}
info := bindataFileInfo{name: "pieces/bB.svg", size: 1132, mode: os.FileMode(420), modTime: time.Unix(1445797333, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
var _piecesBkSvg = []byte("\x1f\x8b\x08\x00\x00\x09\x6e\x88\x00\xff\xa4\x54\xcb\x92\xdb\x20\x10\xbc\xfb\x2b\x28\x72\xd5\x22\x5e\x7a\xfa\x71\xf1\xd5\xf9\x08\x65\xc5\x4a\x24\x5a\xc9\x25\x61\x2b\xce\xd7\x07\x10\x60\xaf\x13\x3b\x49\x45\x07\xd3\x30\xc3\xd0\xdd\x33\xe5\xcd\x74\x6e\xc0\xf7\xf7\xae\x9f\xb6\xb0\x55\xea\x58\xc6\xf1\x3c\xcf\x68\x66\x68\x18\x9b\x98\x62\x8c\x63\x9d\x01\xc1\x59\x8c\x93\x1c\xfa\x2d\x24\x88\x40\x30\xcb\x5a\xb5\x5b\xc8\x13\x08\x5a\x21\x9b\x56\x59\xbc\x5b\x01\xb0\x69\xc0\xa4\x2e\x9d\xd8\xc2\x37\xd9\x75\x65\x3f\xf4\x62\x0d\x0c\x7c\x19\x8e\xd5\xab\x54\x97\x92\xb8\xfd\x78\xea\x44\x29\xce\xa2\x1f\xea\x7a\xad\x2f\x8d\xc3\x37\x51\x7e\xc2\xf6\xf3\xfb\x17\xfb\x50\x49\x50\x12\x4e\x3a\xd9\x8b\xd7\xea\x58\x8e\xc3\xa9\xaf\xd7\x37\x87\x5f\x07\xd9\x7f\x3c\x7d\x97\x4a\x8c\x9d\xd4\x4b\xc9\xc3\xfd\xba\x9a\xda\x6a\x1c\xab\x8b\xe3\xe6\x8e\xaf\xec\xac\x0c\x2d\xe4\x58\xa9\xd6\x22\xfd\xd5\x5b\xf8\x19\x50\x8a\x92\x88\x10\x94\x32\x70\x58\x36\x29\xf4\x09\xbf\x6a\x7e\x20\x28\x30\xb5\xe4\xd6\xa1\x80\xd4\x4f\x98\x17\xd3\x24\xc3\x10\xc4\x4f\x39\xd0\x04\xec\x03\xa2\x59\x44\x32\xa4\xd7\xc4\xb0\xe3\xc8\xc6\x02\xa6\xdc\x20\xea\xb8\x53\x13\xc3\x0b\x22\x85\xcf\xb9\xa2\x3d\x20\xb9\xab\xe6\xab\x2f\xeb\x6f\x75\x7a\x69\xf7\xdd\xbd\x6b\xd4\x97\x93\x52\x0f\xe5\x3f\x56\xaa\x8d\x4e\x22\x96\x19\x4e\x59\xc4\x31\xb2\x4a\xed\xca\xe8\x12\x39\x38\x84\x75\x8e\x47\x9c\x58\xbe\x26\x2b\x37\xaa\x0a\xab\x8a\x59\x17\x98\xf6\x25\x22\xa9\xd3\xc4\x74\xc4\xb5\x91\x66\x01\xb1\xc5\x85\xc2\xe4\x15\xcb\xa5\xf4\xa6\x8e\x2f\x6e\xc9\xd1\xe2\x03\x3a\x04\xca\x3f\xc0\x53\xbf\xee\x47\xe3\x59\xbb\x71\x94\x1b\x6e\x49\x94\xff\xff\xa8\x3d\x7e\x86\xd1\x45\xc2\x3e\x20\x63\x74\xf0\x11\x33\x63\x40\xee\x9c\x24\x66\x58\xac\x95\xb9\x33\x8d\x07\x2b\x49\x44\x53\x94\x06\x37\xdd\x34\x62\x93\x5a\xa0\x02\xa7\xe6\x66\x8a\x8a\x22\x0b\x05\xf9\x8d\xa5\xb9\x86\xb9\x39\xbe\xc1\x7f\x21\xfb\xcd\x7e\x7f\x9e\x25\xbc\xcc\x92\xee\x36\xb5\xbf\x7e\x66\x7c\xdc\xf5\x3e\xd3\x67\xcb\xb4\xb1\xeb\xb4\x99\xd8\xdd\x4c\x32\x6e\x73\xb8\x9f\xc7\x7f\x66\xba\x89\x9b\xdd\x6a\x63\xfe\x5e\x77\xab\x9f\x01\x00\x00\xff\xff\x43\xf4\x20\xfb\x87\x05\x00\x00")
func piecesBkSvgBytes() ([]byte, error) {
return bindataRead(
_piecesBkSvg,
"pieces/bK.svg",
)
}
func piecesBkSvg() (*asset, error) {
bytes, err := piecesBkSvgBytes()
if err != nil {
return nil, err
}
info := bindataFileInfo{name: "pieces/bK.svg", size: 1415, mode: os.FileMode(420), modTime: time.Unix(1445797339, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
var _piecesBnSvg = []byte("\x1f\x8b\x08\x00\x00\x09\x6e\x88\x00\xff\xac\x54\xcb\x6e\xe2\x4a\x10\xdd\xe7\x2b\x4a\xbe\x9b\x7b\xa5\xa6\xe8\x87\xbb\xed\x76\x20\xd2\x15\xdb\xcc\x47\x58\xc1\x01\xcf\x80\x8d\x8c\x13\x92\x7c\xfd\x9c\xb2\x0d\x64\xa2\x44\x9a\x45\x90\xe8\x7a\x75\x57\x9d\x7a\x79\x71\x7c\xde\xd0\xcb\x7e\xd7\x1c\x97\xc9\xb6\xef\x0f\xc5\x7c\x7e\x3a\x9d\xf8\xe4\xb8\xed\x36\x73\xab\xb5\x9e\xe3\x46\x42\xcf\x55\x77\xac\xdb\x66\x99\x18\x36\x09\x9d\xea\x75\xbf\x5d\x26\xa9\x4f\x68\x5b\xd5\x9b\x6d\x3f\xf0\x77\x37\x44\x8b\x0d\x1d\xfb\xd7\x5d\xb5\x4c\xda\x43\xf9\x50\xf7\xaf\x85\xb9\xa5\xc7\x7a\xb7\x2b\x9a\xb6\xa9\x46\x76\xf6\xc1\x34\xeb\x9e\x76\x55\x51\x3d\x57\x4d\xbb\x5e\xdf\xe2\x7d\xd7\xfe\xaa\x8a\x7f\xf4\xf0\x3b\xcb\xb3\x21\x66\x61\xd8\x5f\x34\xbb\xba\xa9\x1e\xca\x43\xd1\xb5\x4f\xcd\xfa\xf6\x9d\xf2\x67\x5b\x37\x7f\x6a\xf7\x75\x5f\x75\xbb\x1a\xa4\x48\x2f\xef\xd7\xe5\x71\x5b\x76\x5d\xf9\x3a\x61\x9b\xd4\x57\x74\x43\x46\xc8\xe9\x50\xf6\xdb\x81\x23\x5a\x2f\x93\x1f\x64\xad\x32\x9a\x56\xe4\x2c\x7b\x65\x0c\xb9\x5c\x68\x0e\xaa\x5c\xa4\x7b\x32\x5e\xe8\x6a\xa0\x9a\x2c\x4e\x5c\x24\xeb\x70\x27\x99\xfc\x4c\x45\x1a\x2a\xf3\x21\xd3\x8b\x9c\xd0\xfc\x2b\x00\xa9\x84\x5b\x81\x32\x62\x5a\xcd\xd1\x90\xc9\x39\xf5\xca\x7a\x76\x19\x99\xa0\x6c\x26\x00\x9c\xb2\x11\x27\x1b\x20\x33\xec\x52\x32\x06\x0c\x2c\x91\xa3\x87\x4e\xb3\x0e\x64\x2c\xa7\x06\x0f\x38\x06\xb1\x5b\xf1\x6c\xb4\x50\x83\x6e\x47\xb8\x60\xeb\x44\xe3\x24\xe9\x28\xc4\x73\x8c\x99\x78\x42\xa0\x00\x25\x08\x7c\xa3\x2c\xe7\x73\x75\x96\x1c\xe7\x51\x21\x02\x00\x02\xb4\x46\x1d\x04\x16\xdb\x4c\x45\xf6\x12\xdc\xa1\x76\xa8\xdf\xc8\x64\xa3\x3d\x05\x1b\x44\x17\xa4\xb0\xfa\x42\xef\x25\x4b\x3f\x16\xff\xcc\x19\xa0\xcb\xe1\x41\xeb\x9c\xac\x51\x92\x36\xfa\x93\x4d\x4d\x1a\xce\xef\x29\x7a\x94\x1e\x7a\x1c\xff\x93\xa4\x31\xfc\x09\x69\x91\x00\xf9\xd4\x70\x79\xf1\xf6\x29\x82\xc7\xe1\x77\x45\x30\xc9\x5f\x23\x30\xa8\xc8\x35\x8e\xb9\xc4\x19\x4a\xfb\xb9\x61\x7a\x71\x01\xd0\x77\x65\x73\x7c\x6c\xbb\xfd\x32\xd9\x97\x7d\x57\xbf\xfc\xab\x39\x0f\x41\xe1\x9d\x9a\xc9\x31\x8a\x91\x43\x74\x6a\xe6\xd9\x64\xee\xbf\xef\x01\x8f\x59\xf5\xd2\x30\x4e\xd1\x46\x08\x06\x9b\xc3\xb9\x1f\x85\x80\x11\x91\xc6\x65\x9c\xe1\x8e\x23\x0c\x26\xd6\x06\x73\x90\xc6\x69\xcb\x72\x58\x64\xe5\x52\xb9\x61\x1d\x6b\xac\x9d\x1f\xf8\x28\x33\x0c\xde\xfa\x71\xfb\x84\x05\xc7\xe2\xda\x65\xb2\x14\x57\x61\x5a\x4c\x59\x9a\x9c\x63\x4a\x2e\x70\xb0\xca\x62\x3c\xbd\xb8\x96\x98\x19\x07\x99\x68\xac\x4b\x9e\x2b\x59\x1d\x8c\x12\x56\x2b\x08\x5c\x6d\xa5\x9f\x3a\x8c\x83\x7c\xff\x3e\xa7\x37\xfa\x9b\x32\x0d\x1f\x9a\xa9\x46\x8b\xf9\xe6\xee\x66\x21\x9f\xd7\xbb\x9b\xdf\x01\x00\x00\xff\xff\x7e\xb7\x48\xaf\x87\x05\x00\x00")
func piecesBnSvgBytes() ([]byte, error) {
return bindataRead(
_piecesBnSvg,
"pieces/bN.svg",
)
}
func piecesBnSvg() (*asset, error) {
bytes, err := piecesBnSvgBytes()
if err != nil {
return nil, err
}
info := bindataFileInfo{name: "pieces/bN.svg", size: 1415, mode: os.FileMode(420), modTime: time.Unix(1445797343, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
var _piecesBpSvg = []byte("\x1f\x8b\x08\x00\x00\x09\x6e\x88\x00\xff\x5c\x92\xcd\x72\x9b\x30\x10\xc7\xef\x7e\x8a\x1d\xf5\x8a\x05\xfa\x02\xa4\x18\x5f\x72\x6d\x1f\x82\x89\x55\xa3\x96\x20\x46\x28\xa1\xce\xd3\x77\x25\x08\x99\x96\x03\xfa\xed\x7f\x3f\xb4\xbb\x70\x59\xde\xef\xf0\xe7\x75\x9c\x96\x8e\x0c\x31\xce\xa6\x2c\xd7\x75\xa5\xab\xa0\x3e\xdc\x4b\x5e\x55\x55\x89\x11\x04\xde\x6d\x58\x9c\x9f\x3a\xc2\x28\x23\xb0\xba\x5b\x1c\x3a\x22\x15\x81\xc1\xba\xfb\x10\x33\x5f\x4f\x00\x97\xb9\x8f\x03\x9e\x00\xb7\x8e\xfc\x00\xce\x0b\x0d\xcf\xc0\x34\x6d\x34\x12\x6b\x0b\x56\x21\x66\x10\xc9\x91\x4e\xda\x26\x81\x72\x5d\x30\x49\x1b\x96\xb8\x41\x5d\x51\xd1\xa6\x90\x9a\xb6\xa2\xc0\xb7\x02\x94\x54\x81\x5e\xa5\x37\xe4\x2c\xf9\x33\x09\x5a\x89\x14\x2a\x65\xc1\x25\x6d\x25\xb0\x86\x6a\x56\xf0\x3a\xe9\x18\x24\xb3\xd5\xd0\x0a\x53\x2b\x4c\x10\x8c\xaa\x76\x47\x8d\xa5\xbf\x83\x10\x9f\xfc\xbc\x73\x0e\xe1\x1a\x73\xf6\xcc\x54\x4d\x1f\x35\x51\x53\xf5\x7e\x1b\x6f\x8f\x26\x36\x4c\x9d\x65\xda\xda\xc5\x58\xd6\x6c\x43\x70\x45\x71\x2b\x9f\xd3\xa1\xd5\xb0\x7d\x6e\x5e\xef\xcb\xc8\x90\x9c\xf5\xbe\x2e\xbc\x84\x33\xdc\x5f\x5e\xe7\x07\x90\xbc\xe0\x25\x3e\x46\xdb\x11\x3f\xf7\x2f\x2e\x3e\x0c\x7b\x82\x9f\x6e\x1c\xcd\xb7\x2a\x3f\x9b\x75\xfe\xcf\x7b\x0e\x6f\xa3\x35\x93\x9f\x3e\x6c\xf0\x4f\x58\x22\xf8\xdf\xf6\x2b\x65\xb3\xcf\xf9\xf3\x1a\x1c\xff\x50\x46\x37\xd9\x97\x7e\x36\xc1\xbf\x4d\xb7\x7f\xd4\x5f\xde\x4d\xe6\xd5\x45\x1b\x0e\x39\x5b\xa3\xc3\xc3\xc8\x43\xbc\xf5\xcb\xd0\x87\xd0\x3f\xd2\xed\xf6\x90\xbf\xfa\x23\x50\x5e\x4f\x97\xf4\xb7\x5d\x4f\x7f\x03\x00\x00\xff\xff\x4f\x2d\x50\x9e\x96\x02\x00\x00")
func piecesBpSvgBytes() ([]byte, error) {
return bindataRead(
_piecesBpSvg,
"pieces/bP.svg",
)
}
func piecesBpSvg() (*asset, error) {
bytes, err := piecesBpSvgBytes()
if err != nil {
return nil, err
}
info := bindataFileInfo{name: "pieces/bP.svg", size: 662, mode: os.FileMode(420), modTime: time.Unix(1445797348, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
var _piecesBqSvg = []byte("\x1f\x8b\x08\x00\x00\x09\x6e\x88\x00\xff\xac\x55\xc1\x72\x9b\x30\x10\xbd\xe7\x2b\x34\xea\x15\x0b\x49\x0b\xd8\x60\x93\x99\x8e\xaf\xe9\x47\x50\x50\x0c\x2d\x01\x0f\xc6\x76\xdc\xaf\xef\x4a\x48\xe0\x90\xd6\x71\x66\x82\x0f\xbb\xda\x7d\xda\x7d\xfb\x24\xf0\xe6\x70\xda\x91\xd7\x97\xba\x39\xa4\xb4\xec\xfb\x7d\xe2\xfb\xe7\xf3\x99\x9d\x81\xb5\xdd\xce\x97\x9c\x73\x1f\x11\x94\x9c\x54\x77\xa8\xda\x26\xa5\x82\x09\x4a\xce\x55\xd1\x97\x29\x0d\x42\x4a\x4a\x55\xed\xca\xde\xf8\x8f\x0f\x84\x6c\x76\xe4\xd0\x5f\x6a\x95\xd2\x76\x9f\xe5\x55\x7f\x49\xc4\x9a\x3c\x57\x75\x9d\x70\xf3\x0c\x8b\xc5\x2c\xb9\xe8\x8e\xb5\x4a\xd4\x49\x35\x6d\x51\xac\xb1\x42\xd7\xfe\x56\xc9\x37\xb7\x65\x58\x2f\x4c\xd7\x44\xb0\x70\x8c\xd4\x55\xa3\xf2\x6c\x9f\x74\xed\xb1\x29\xd6\x57\xc1\x5f\x6d\xd5\xbc\x8d\xbe\x54\xbd\xea\xea\x0a\x4d\x12\x8c\xfb\x8b\xec\x50\x66\x5d\x97\x5d\x92\xa6\x6d\xd4\x18\x9e\xd8\x99\x99\xae\xa7\x32\xa3\xcc\x88\x0d\x9b\x2d\x14\xc1\x79\xd5\xe5\xb5\x22\xf9\x6b\x4a\x23\xaa\x23\xf9\x05\x75\x93\x94\x74\x29\x95\x6c\x89\xa2\xf9\xff\xc2\x8a\x80\x5a\x6c\x8c\xce\x6d\xac\x94\x0c\x53\x1a\xbb\xfa\x10\x0b\xe2\xfe\xba\x10\xd3\x1b\x7c\x37\xfe\xce\x3a\xfb\xac\x2f\xed\x76\x52\xa4\xf4\x07\x89\x3d\x19\x91\x2d\x11\x4b\x16\x7a\x32\x60\x21\x01\x6e\x6d\xa4\x33\x4f\x04\x56\x98\x11\x80\x11\xf4\x85\x27\x8d\xe5\x6c\xe9\x09\xce\x62\xf4\x65\xe8\x76\xa2\x2f\x35\x96\xa3\x27\xe2\x29\x2a\x02\x06\x0e\x2d\x82\xa1\x42\x34\x15\x35\x0c\xfe\x50\xc7\xca\x9e\xd7\xec\xa2\xfc\x3c\xf6\xfd\xbb\xfb\x35\xcd\xf7\xbf\xb1\xd0\xac\x08\xb6\x0e\x8d\xc5\x1b\xe8\x01\xd7\xd3\x6a\x9e\x80\x4b\xe7\xa1\xf5\x40\xd3\xd9\x0e\x68\xd0\xcc\x07\x2f\x1a\xad\x2e\x07\x4b\x9d\x10\x9e\x56\x65\xb4\x56\x3e\xc0\x99\x89\x1c\x3d\x08\x5c\xd6\x79\xa0\xa5\x32\x15\x74\x24\xb2\x99\x88\xe8\x6e\x43\x4b\x00\x47\x03\x2c\x31\x18\xa9\xea\xc4\x40\xdf\xe0\x71\x20\x7d\x44\x2b\x7b\x50\xdb\xa1\xb3\x91\x7c\x3a\xcc\xfb\xb5\xbd\xa1\xa5\x1b\xf3\x3b\x0e\xe0\x01\x96\x27\x1c\x7f\x76\xa8\x79\x6d\xf3\x9e\x5d\xbf\x94\xef\xbe\x06\x9f\x68\x2b\xe3\x37\x4d\x85\x6e\x2a\xe3\x3b\x5a\x3e\x9b\xe7\x66\xf5\x51\xd7\xa7\x49\xe3\xaf\xa9\x2c\xdc\x69\xce\x04\x03\x1b\xff\x9a\x2e\xdc\xdd\xa5\xf9\xb1\xd8\xf8\xa7\xbb\x98\x8f\xc4\x46\xff\x6b\x3c\x3e\xfc\x0d\x00\x00\xff\xff\xd0\x03\xeb\x88\x5e\x06\x00\x00")
func piecesBqSvgBytes() ([]byte, error) {
return bindataRead(
_piecesBqSvg,
"pieces/bQ.svg",
)
}
func piecesBqSvg() (*asset, error) {
bytes, err := piecesBqSvgBytes()
if err != nil {
return nil, err
}
info := bindataFileInfo{name: "pieces/bQ.svg", size: 1630, mode: os.FileMode(420), modTime: time.Unix(1445797352, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
var _piecesBrSvg = []byte("\x1f\x8b\x08\x00\x00\x09\x6e\x88\x00\xff\xbc\x55\xd1\xb2\x9b\x20\x10\x7d\xcf\x57\xec\x70\x5f\x13\x15\x51\x67\x44\xcd\x17\xb4\x1f\x61\xaf\x5c\xa5\xf5\x82\x83\x24\x36\xfd\xfa\x22\x2a\x1a\x67\xda\xc9\x4c\xda\xf8\x72\x0e\x0b\xec\xd9\x65\x77\xc7\xbc\xbf\xd6\xf0\xf3\xb3\x15\x7d\x81\x1a\xad\x3b\xea\xfb\xc3\x30\x78\x03\xf1\xa4\xaa\xfd\x30\x08\x02\xdf\x9c\x40\x70\x65\xaa\xe7\x52\x14\x08\x7b\x18\xc1\xc0\x2b\xdd\x14\x28\x8a\x11\x34\x8c\xd7\x8d\xb6\xfc\x7c\x00\xc8\x6b\xe8\xf5\xad\x65\x05\x92\x5d\xf9\xce\xf5\x8d\xe2\x0c\x3e\x78\xdb\xd2\xc0\x7e\xd3\xe2\xb4\xdb\x3c\xa9\x4b\xcb\x28\xbb\x32\x21\xab\x2a\x33\x1e\x94\xfc\xc1\xe8\xdb\x72\x65\x5a\x9f\xac\x2a\xc5\x5e\xec\x2c\x2d\x17\xec\xbd\xec\xa8\x92\x17\x51\x65\x1b\xe3\x77\xc9\xc5\xbd\xf5\x93\x6b\xa6\x5a\x6e\x80\x46\xee\x7e\x55\xf6\x4d\xa9\x54\x79\xa3\x42\x0a\xe6\xcc\x6b\x74\x36\x27\x93\x55\x57\xea\xc6\x32\x80\xaa\x40\x5f\x21\x3d\x92\x14\xbe\x00\x49\x56\x4c\x0c\xa6\x0e\x52\xf8\x05\x68\xbe\x31\x3f\xc8\x2e\xe6\x6f\x17\xad\x33\x04\xfe\x1f\x14\x70\xe8\xc5\x47\x12\x1a\x6f\x38\x3a\x86\xa9\x17\x8f\x32\xd8\xb1\x75\x77\x66\x4f\xcb\x4d\xa1\x8f\x38\xba\x25\x64\xc5\xc5\x9e\x3c\x2d\xe2\x32\x31\x0c\x27\x4b\x4e\x2b\x5b\x77\x2d\x7b\x48\x6e\x5f\x75\x5b\xe8\xbf\x07\x31\x0b\x62\x23\x1d\x8d\xc2\xd1\x8c\x2e\x94\xe5\xcc\xb3\xf9\xce\x02\x06\xc7\x2e\xc1\xf1\x02\x18\x1b\x0c\x03\x87\xa3\x39\x8c\x17\xb0\x56\x12\x38\x4c\xa7\x18\xd3\x4d\xa8\x93\xe7\x7f\x50\xf3\x78\x7a\x7a\xb2\x63\x3b\xbf\x76\x80\xb7\x23\x42\xdf\x3e\xec\xb7\x9f\xcd\xbb\xc9\x7c\xac\x1c\x46\x10\xcf\x3d\x6d\xd9\x0b\xa5\xf7\x83\xf5\x52\xe9\xfb\xfe\x7f\xa1\xf4\xb6\xeb\xff\x93\x6c\xee\xd7\xe7\x43\x3e\xfe\x39\xce\x87\xdf\x01\x00\x00\xff\xff\xbd\x2e\xf0\x59\x62\x06\x00\x00")
func piecesBrSvgBytes() ([]byte, error) {
return bindataRead(
_piecesBrSvg,
"pieces/bR.svg",
)
}
func piecesBrSvg() (*asset, error) {
bytes, err := piecesBrSvgBytes()
if err != nil {
return nil, err
}
info := bindataFileInfo{name: "pieces/bR.svg", size: 1634, mode: os.FileMode(420), modTime: time.Unix(1445797357, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
var _piecesWbSvg = []byte("\x1f\x8b\x08\x00\x00\x09\x6e\x88\x00\xff\x84\x54\xdd\x72\x9b\x3c\x10\xbd\xcf\x53\xec\x90\x5b\x47\xe8\x07\x89\x9f\xd8\x99\xf9\xe6\xbb\xb5\x1f\x82\x06\xc5\xd0\x12\xf0\x80\x6c\xd7\x7d\xfa\x6a\xa5\x85\xb8\x9d\xb4\xc5\x63\xb4\x3e\x3a\x7b\xce\x59\x0d\x78\x3b\x5f\x8e\xf0\xfd\xbd\x1f\xe6\x5d\xd2\x3a\x77\xaa\xd2\xf4\x7a\xbd\xb2\xab\x62\xe3\x74\x4c\x25\xe7\x3c\xf5\x8c\x04\x2e\x76\x9a\xbb\x71\xd8\x25\x82\x89\x04\xae\x5d\xe3\xda\x5d\x92\xe9\x04\x5a\xdb\x1d\x5b\x17\xea\x97\x07\x80\xed\x11\x66\x77\xeb\xed\x2e\x19\x4f\xf5\x6b\xe7\x6e\x95\x78\x86\xb7\xae\xef\xab\x61\x1c\x6c\x2c\x9f\xa6\x73\x6f\x2b\x7b\xb1\xc3\xd8\x34\x04\xdd\xb1\x67\x37\x8d\xdf\x6c\xf5\xc8\xc3\xb5\xfc\x7e\x0a\x9e\x95\x60\x7a\x45\xfa\x6e\xb0\xaf\xf5\xa9\x9a\xc6\xf3\xd0\xfc\x82\x7e\x1d\xbb\xe1\x37\xf8\xbd\x73\x76\xea\x3b\xbf\x54\xd9\x0a\x36\xf5\xdc\xd6\xd3\x54\xdf\x28\x1d\xc1\x1f\x61\xc2\x4c\xf7\x53\x85\x51\x1e\xdf\xc2\xf5\xc7\xa8\x4b\xb0\x2f\x67\xe7\x16\x09\x2f\x72\xaa\x5d\x4b\x35\x40\xb3\x4b\x0e\x50\x6e\x94\x81\xff\x41\x48\xa6\x7c\xa9\x19\x57\x20\x4a\x26\x84\x87\x59\xa6\x40\x4a\xa6\x37\x2a\xf3\x0c\xa9\x59\x51\x12\xaa\x24\x33\x82\xd8\xca\x44\x85\xb8\xaa\x9c\x19\x8d\x2c\x9d\x01\x0a\x16\xb8\x53\x30\x25\x7d\xc9\xca\x1c\xf7\x95\x0e\x75\x19\x3a\x0a\xa6\x91\x11\xf5\x72\xa6\xd5\xe2\xe3\x19\x86\xdc\xf3\xc0\xa1\x54\x01\xa7\xb4\x81\x5f\x2e\x22\xde\x39\x33\x24\x6d\x98\xc9\x73\xb2\x34\x31\x05\x1a\x67\x98\x8c\x9b\x38\x74\xb8\xfd\x48\x20\xfd\xcb\xe9\x08\x6f\x2f\xd1\x3c\x0f\xc7\xe0\x7d\xe4\x5a\x29\x1e\xf7\x14\x47\x84\x13\xc2\xe9\x8e\xf8\x06\xc9\xb1\x43\x9a\x75\xf5\x3b\x6a\x23\x83\x82\xf2\x88\x08\xaa\x38\xa8\xe0\x71\x50\xb1\xa0\x42\x46\x9e\x58\x3b\x97\xca\xe7\x0a\xda\x98\x0f\xbd\xe2\x8a\x4d\x31\x49\xcc\xfd\x8f\xe1\xa4\x86\x02\xfe\x03\x89\xfe\xfe\xeb\xfb\xfd\x07\x24\xdf\x7c\x86\x22\xf7\x43\x6f\x9b\x1e\xa9\xb8\x93\x8d\x27\x46\x09\xf7\xeb\xbc\x07\x4a\xb7\xa7\x93\x39\xd0\xb4\xda\xab\xef\x63\x2d\x31\xf3\x01\xad\x45\x81\x98\xdf\x2d\x12\x52\xbd\x7f\xea\xef\x5f\x91\x4f\x1f\xf9\xf0\xd6\x85\xf7\xec\x99\xb2\x86\xa4\x5b\xfc\x07\x79\x79\xf8\x19\x00\x00\xff\xff\xd8\xa0\xa7\x83\x6a\x04\x00\x00")
func piecesWbSvgBytes() ([]byte, error) {
return bindataRead(
_piecesWbSvg,
"pieces/wB.svg",
)
}
func piecesWbSvg() (*asset, error) {
bytes, err := piecesWbSvgBytes()
if err != nil {
return nil, err
}
info := bindataFileInfo{name: "pieces/wB.svg", size: 1130, mode: os.FileMode(420), modTime: time.Unix(1445797361, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
var _piecesWkSvg = []byte("\x1f\x8b\x08\x00\x00\x09\x6e\x88\x00\xff\xb4\x54\x4d\x73\x9b\x30\x10\xbd\xfb\x57\xec\x28\x57\x22\xf4\x01\xc6\x60\xe3\x4b\xae\xee\x8f\xa0\x41\x01\xb5\x04\x3c\x20\x9b\xba\xbf\xbe\xd2\x22\x70\x32\x6d\xea\x7a\xa6\xe1\x60\x3d\x69\x57\xbb\xef\xbd\xd5\x78\x37\x9c\x2b\xf8\xf1\xda\xb4\x43\x4e\x6a\x63\x8e\x59\x18\x8e\xe3\x48\x47\x49\xbb\xbe\x0a\x05\x63\x2c\xb4\x19\x04\xce\xaa\x1f\x74\xd7\xe6\x84\x53\x4e\x60\xd4\xa5\xa9\x73\x12\xc5\x04\x6a\xa5\xab\xda\x20\xde\xaf\x00\x76\x15\x0c\xe6\xd2\xa8\x9c\xbc\xe8\xa6\xc9\xda\xae\x55\x5b\x70\xf0\xb1\x3b\x16\xcf\xda\x5c\x32\xee\xf7\xfd\xa9\x51\x99\x3a\xab\xb6\x2b\xcb\xad\xbd\xd4\x77\xdf\x55\xf6\xc0\xf0\x9b\xf7\x8f\xd8\x28\xe3\x34\x5e\x4e\x1a\xdd\xaa\xe7\xe2\x98\xf5\xdd\xa9\x2d\xb7\x6f\x0e\xbf\x75\xba\x7d\x7f\xfa\xaa\x8d\xea\x1b\x6d\x97\x2c\x5a\xee\x97\xc5\x50\x17\x7d\x5f\x5c\x3c\x37\x7f\x7c\x65\x87\x32\xac\x90\x63\x61\x6a\x44\x00\x65\x4e\xbe\x80\x10\x34\x0e\x38\xa7\x6b\x09\x87\x69\xb3\x26\x3e\xfe\xbb\xe2\x0f\xe4\x2c\x3c\x91\xda\x96\x40\xf8\x51\x33\x16\x6c\x5c\x9b\x38\xd8\x7c\x62\x13\x27\x42\xc4\xf0\xb4\x20\x91\x04\x3c\xa1\x76\x8d\x9d\xd6\x88\x62\x6c\xc1\x22\x72\x48\x78\x27\x84\x8b\xb1\x09\xf1\x74\xce\xb9\xa2\x27\xe0\x1b\x5f\x6d\xae\x3e\xad\x7f\x12\xf4\xf0\x82\xdf\x5f\x35\xb9\xa9\x7f\x3d\x19\x73\xbf\x50\x3b\xb5\x38\x90\x89\xa3\x94\x04\x11\xa3\x28\x14\x57\x29\xa6\xc8\xc1\x23\x66\x73\x66\x14\x71\xa4\xeb\xb2\x36\x4e\x54\x8a\xa2\x24\x9a\x20\xdd\x68\xf8\xda\x4b\x92\x36\xe2\xdf\x84\x48\x16\x24\x27\x13\x52\x97\x97\x4e\x97\xd6\x6f\xea\xcc\xc5\x91\x9c\x48\xdf\xa1\xc3\x42\xf9\x27\xdc\x63\xd7\x2d\x0b\xd8\x64\x81\x25\x29\xf0\xd7\x4b\xfd\xe7\x27\x76\xab\xbe\x97\x9c\xd8\x9a\x93\xc9\xf2\x6a\xb2\x8d\xfd\xb7\x3e\x7e\x94\x32\xc2\x1e\xd1\x3c\xc6\x7b\xeb\xef\xc2\x6a\xbf\xda\xb9\x3f\xb8\xfd\xea\x57\x00\x00\x00\xff\xff\xdb\x19\xf5\xd8\x09\x05\x00\x00")
func piecesWkSvgBytes() ([]byte, error) {
return bindataRead(
_piecesWkSvg,
"pieces/wK.svg",
)
}
func piecesWkSvg() (*asset, error) {
bytes, err := piecesWkSvgBytes()
if err != nil {
return nil, err
}
info := bindataFileInfo{name: "pieces/wK.svg", size: 1289, mode: os.FileMode(420), modTime: time.Unix(1445797369, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
var _piecesWnSvg = []byte("\x1f\x8b\x08\x00\x00\x09\x6e\x88\x00\xff\xac\x54\x61\x6f\x9b\x4c\x0c\xfe\xde\x5f\x61\xd1\x2f\xef\x2b\x5d\xcc\xf9\x8e\x03\x8e\x86\x48\x53\xbe\x76\x3f\x02\x15\x12\xd8\x08\x44\x40\x93\x66\xbf\x7e\xbe\x0b\xa1\xeb\x96\x49\xd3\x34\xa4\xfa\xf1\xd9\x67\xfb\xf1\x73\x55\xd6\xe3\x69\x0f\x6f\x87\xb6\x1b\xf3\xa0\x9e\xa6\x63\x16\x86\xe7\xf3\x19\xcf\x1a\xfb\x61\x1f\x2a\x29\x65\xc8\x37\x02\x38\x55\xc3\xd8\xf4\x5d\x1e\x10\x52\x00\xe7\xa6\x9c\xea\x3c\x88\x4c\x00\x75\xd5\xec\xeb\xc9\xfb\x9b\x07\x80\xf5\x1e\xc6\xe9\xd2\x56\x79\xd0\x1f\x8b\x97\x66\xba\x64\xf4\x04\xbb\xa6\x6d\xb3\xae\xef\xaa\xab\xbb\xfa\x29\xb5\x1a\x5e\xdb\x2a\xab\x4e\x55\xd7\x97\xe5\x13\xd7\x0f\xfd\xd7\x2a\x7b\x94\xfe\xbb\x9d\x57\x7e\x66\x46\x68\x96\x48\xdb\x74\xd5\x4b\x71\xcc\x86\xfe\xb5\x2b\x9f\x7e\x08\x7e\xe9\x9b\xee\x63\xf4\xd0\x4c\xd5\xd0\x36\x0c\x59\xb4\xd4\x97\xc5\x58\x17\xc3\x50\x5c\x66\x6e\x73\xf8\x9d\x9d\xdf\x88\x77\x3a\x16\x53\xed\x3d\x80\x32\x0f\x3e\x83\x52\x82\x24\x6c\x41\x2b\x34\x82\x08\x74\xea\x30\x65\x14\xda\xc2\x33\x90\x71\xb8\xf5\x28\x41\xb1\xe5\x8b\xa0\x34\xdf\x09\xe6\x3e\xb3\x48\x5e\x99\xc7\x9d\xff\x7e\xd9\x3c\x80\xf0\x77\x04\x22\x37\x6e\xcb\x88\x3c\x53\x49\xb4\x04\x94\x62\x64\x84\x32\xa8\x13\xa0\x58\xa8\xc4\x11\xd0\x42\x59\xb6\x48\xcc\x8c\x50\x47\x40\xc4\x0e\x67\x2c\x5a\xc3\x31\x89\x32\x06\x52\x18\x11\x17\xa0\x8d\x5d\x5e\xb9\xce\x24\x1d\x12\xbf\xb6\xe5\x16\xa8\xb4\x8b\x68\xb7\xb4\x75\x60\xd0\xda\xc4\x75\xe2\x41\x31\x07\x19\xb8\x37\xcb\x72\xb3\xdb\xdb\x49\x63\x6a\x05\x4f\x60\x82\x4c\x5a\xb2\x0e\x8e\x16\xaa\x44\x58\x34\x6e\xb8\x66\xed\x58\xbf\xab\x93\x5c\xf3\x11\xbb\xb1\x8b\xc5\x4e\x58\xb9\xe0\xb3\xdb\xd2\x5c\xc5\xbf\x79\xc4\xec\x52\xee\x20\x65\x0a\x8a\x84\x5b\x9b\xdf\x27\x99\x1f\xc9\xdb\x7f\x23\xba\x75\x6f\x68\xd8\x7c\x02\xb7\x86\xff\x03\x5e\x0b\x1c\x91\xbb\x89\xa5\xe2\xdb\x5d\x06\x1f\xff\xc1\xff\x80\x01\xb1\x22\xef\x73\x68\x99\xe3\xa5\xbd\x9f\x98\x2b\x16\x02\xd3\x50\x74\xe3\xae\x1f\x0e\x79\x70\x28\xa6\xa1\x79\xfb\x4f\x62\x1a\xc7\x82\xeb\xc4\xca\x99\xeb\xd1\x62\x6c\xb5\x58\x19\xa4\x44\xff\xff\x17\xe4\xd7\xe1\x7e\xf3\xb0\x76\x3f\x1d\x9b\x87\xef\x01\x00\x00\xff\xff\xe4\xc9\xe6\xad\x63\x04\x00\x00")
func piecesWnSvgBytes() ([]byte, error) {
return bindataRead(
_piecesWnSvg,
"pieces/wN.svg",
)
}
func piecesWnSvg() (*asset, error) {
bytes, err := piecesWnSvgBytes()
if err != nil {
return nil, err
}
info := bindataFileInfo{name: "pieces/wN.svg", size: 1123, mode: os.FileMode(420), modTime: time.Unix(1445797366, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
var _piecesWpSvg = []byte("\x1f\x8b\x08\x00\x00\x09\x6e\x88\x00\xff\x5c\x92\xcd\x72\x9b\x30\x10\xc7\xef\x7e\x8a\x1d\xe5\x8a\x05\xfa\x02\xa4\x18\x5f\x72\x6d\x1f\x82\x89\x15\xa3\x96\x20\x8f\x50\x42\x9d\xa7\xef\x4a\x60\x3a\x8d\x0e\xd6\x6f\xff\xfb\xa1\xdd\x35\xa7\xf9\xf3\x0a\x7f\xde\xc7\x69\xee\xc8\x10\xe3\xcd\x94\xe5\xb2\x2c\x74\x11\xd4\x87\x6b\xc9\xab\xaa\x2a\x31\x82\xc0\xa7\x0d\xb3\xf3\x53\x47\x18\x65\x04\x16\x77\x89\x43\x47\xa4\x22\x30\x58\x77\x1d\x62\xe6\xf3\x01\xe0\x74\xeb\xe3\x80\x37\xc0\xa5\x23\x3f\x81\xf3\x42\xc3\x0b\x30\x4d\x1b\x8d\xc4\xda\x82\x55\x88\x19\x44\x72\xa4\x9b\xb6\x49\xa0\x5c\x17\x4c\xd2\x86\x25\x6e\x50\x57\x54\xb4\x29\xa4\xa6\xad\x28\xf0\x57\x01\x4a\xaa\x40\xaf\xd2\x2b\x72\x96\xfc\x99\x04\xad\x44\x0a\x95\xb2\xe0\x92\xb6\x12\x58\x43\x35\x2b\x78\x9d\x74\x0c\x92\xd9\x6a\x68\x85\xa9\x15\x26\x08\x46\x55\xbb\xa1\xc6\xd2\x3f\x40\x88\x07\xbf\x6c\x9c\x43\xb8\xc6\x9c\x2d\x33\x55\xd3\x7b\x4d\xd4\x54\xbd\xbd\xc6\xdb\xbd\x89\x15\x53\x67\x99\xd6\x76\x31\x96\x35\xeb\x10\x5c\x51\xdc\xca\x63\x3a\xb4\x1a\xb6\xcd\xcd\xeb\x6d\x19\x19\x92\xb3\xde\xd6\x85\x8f\x70\x86\xfb\xcb\xeb\xfc\x02\x92\x17\x3c\xc7\xfb\x68\x3b\xe2\x6f\xfd\xab\x8b\x77\xc3\x9e\xe1\xcd\x8d\xa3\x79\x7a\xcb\x67\xb5\x8e\xdf\xbc\xc7\xf0\x31\x5a\x33\xf9\xe9\xcb\x06\xff\x8c\x25\x82\xff\x6d\xcd\x53\x95\xcf\xc3\x3e\xe6\xbf\xd7\xe0\xf8\xbb\x32\xba\xc9\xbe\xf6\x37\x13\xfc\xc7\x74\xf9\x4f\xfd\xe5\xdd\x64\xde\x5d\xb4\x61\x97\xb3\x35\x3a\xbc\x8c\xdc\xc5\x4b\x3f\x0f\x7d\x08\xfd\x3d\xbd\x6e\x77\xf9\x5f\x7f\x04\xca\xf3\xe1\x94\xbe\xb6\xf3\xe1\x6f\x00\x00\x00\xff\xff\x88\xb7\x8e\xde\x96\x02\x00\x00")
func piecesWpSvgBytes() ([]byte, error) {
return bindataRead(
_piecesWpSvg,
"pieces/wP.svg",
)
}
func piecesWpSvg() (*asset, error) {
bytes, err := piecesWpSvgBytes()
if err != nil {
return nil, err
}
info := bindataFileInfo{name: "pieces/wP.svg", size: 662, mode: os.FileMode(420), modTime: time.Unix(1445797373, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
var _piecesWqSvg = []byte("\x1f\x8b\x08\x00\x00\x09\x6e\x88\x00\xff\xbc\x54\xcd\x8e\x9b\x30\x10\xbe\xe7\x29\x46\xde\x4b\x2b\x99\x1f\xdb\x40\x80\x84\x48\x55\xae\xed\x43\xd0\xc5\x01\x5a\x02\x91\x71\x42\xb3\x4f\xdf\xb1\xf9\x49\x9a\xc3\xee\xa5\x1b\x2c\xe5\xfb\x3c\x63\xcf\x7c\x33\x8e\xbd\xed\x2f\x25\xfc\x39\x36\x6d\x9f\x91\x4a\xeb\x53\xea\x79\xc3\x30\xb8\x83\x70\x3b\x55\x7a\xdc\xf7\x7d\x0f\x57\x10\xb8\x48\xd5\xd7\x5d\x9b\x11\xe6\x32\x02\x43\x5d\xe8\x2a\x23\x41\x48\xa0\x92\x75\x59\x69\xcb\x77\x2b\x80\x6d\x09\xbd\xbe\x36\x32\x23\xdd\x29\x7f\xad\xf5\x35\x65\x1b\x38\xd4\x4d\x93\xbe\x1c\xec\x37\xce\x9c\x07\xaf\xa3\xce\x8d\x4c\xe5\x45\xb6\x5d\x51\x6c\x30\x84\xea\x7e\xcb\xf4\xc5\xb7\xdf\x3c\x77\x6c\xda\x94\xb9\xe1\x62\x69\xea\x56\xbe\xe6\xa7\x54\x75\xe7\xb6\xd8\xdc\x19\x7f\x75\x75\xfb\xaf\xf5\x58\x6b\xa9\x9a\x1a\x21\x0d\x96\xfd\x45\xde\x57\xb9\x52\xf9\x35\x6d\xbb\x56\x2e\xe6\x9b\x3a\x5b\x14\x96\x75\xca\x75\x65\x19\x40\x91\x91\x1f\x90\x00\x13\xf0\x0d\x38\x0e\x1f\x18\x0e\x08\xe9\xa3\xc5\xae\x79\x23\xd3\x36\xad\xf2\xb6\x3f\x74\xea\x98\x11\x4b\x9b\x5c\xcb\x2f\x0e\xa3\x0e\xfb\x4a\xc0\xfb\xdc\x34\x2c\x74\x43\xea\xe0\xcf\xe7\xa7\x12\xfc\x29\x15\xad\xa9\x13\x3c\xa3\x1c\x1e\x60\xa2\xf7\xd2\x50\x1e\xc1\x1e\xd8\x1a\x1b\xcc\x51\x11\x08\x7f\xc2\xc8\x78\xbe\x83\x88\x29\x0b\x0c\x32\xca\xc3\x11\x19\x43\xe4\xe1\xbc\x03\x39\x47\x9e\x58\xca\x92\x9b\x99\x05\x94\xf9\x33\xb3\x9b\xd7\x63\x2c\x9b\xf4\x0d\x66\xe1\xd3\x85\x7b\xb8\x12\x3f\xcf\x5a\x6f\x3e\x14\x8e\x10\x83\xc9\x62\x11\xaf\x16\x15\xbe\xa9\xc7\x28\x12\x38\x9d\x19\x22\x15\x02\xe7\xfb\x71\xb5\x30\x12\x47\x16\x2d\x68\xc2\x89\xb5\x71\x30\x2a\xe2\x3b\x9c\x1a\x24\x4c\x8d\x7c\x61\x22\x98\xbd\x33\x13\xa6\x29\x36\x82\xb1\x44\x93\x27\x02\x93\x6d\x4c\x29\xc4\x2c\x43\x4c\xc2\xc4\x22\xd5\x38\x46\xf9\x76\x3d\x16\x64\x0e\x21\x9e\x8e\x62\x3f\x66\xb6\xbd\xbd\x1d\xd7\xff\x68\xe5\x5d\xdb\x30\x6a\x62\xff\x02\xc9\xac\xe6\x21\xb2\x7d\x0a\xed\x5b\xf3\x4e\xbc\x5b\xab\x63\x6a\xca\x43\xe5\x23\x4e\xd5\x7f\x14\x73\xeb\x95\xbb\xd5\xd6\x3c\xdc\xbb\xd5\xdf\x00\x00\x00\xff\xff\xe0\xcf\x6d\x6c\xe1\x05\x00\x00")
func piecesWqSvgBytes() ([]byte, error) {
return bindataRead(
_piecesWqSvg,
"pieces/wQ.svg",
)
}
func piecesWqSvg() (*asset, error) {
bytes, err := piecesWqSvgBytes()
if err != nil {
return nil, err
}
info := bindataFileInfo{name: "pieces/wQ.svg", size: 1505, mode: os.FileMode(420), modTime: time.Unix(1445797377, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
var _piecesWrSvg = []byte("\x1f\x8b\x08\x00\x00\x09\x6e\x88\x00\xff\xa4\x93\xdd\x72\xb2\x30\x10\x86\xcf\xbd\x8a\x9d\x78\xaa\x40\xf8\xd1\x21\x80\x57\xf0\x7d\x17\x41\x25\x42\x5a\x4c\x98\x10\xa5\xf6\xea\x9b\x84\x3f\x6b\xad\xed\x8c\x9c\x3c\x9b\xd7\xec\xee\x9b\x35\x49\xdb\x73\x09\xef\xc7\x9a\xb7\x19\xaa\x94\x6a\x88\xeb\x76\x5d\xe7\x74\x81\x23\x64\xe9\xfa\x9e\xe7\xb9\x7a\x07\x82\x33\x95\x2d\x13\x3c\x43\xd8\xc1\x08\x3a\x56\xa8\x2a\x43\x61\x84\xa0\xa2\xac\xac\x94\x8d\x77\x0b\x80\xb4\x84\x56\x5d\x6a\x9a\x21\xd1\xe4\x7b\xa6\x2e\x04\x27\x70\x60\x75\x4d\x96\x07\xfb\xf5\xab\xf5\xcd\xaf\x6b\x79\xaa\x29\xa1\x67\xca\x45\x51\x24\xba\x84\x14\x6f\x94\x2c\x3d\xfb\x8d\xeb\xb5\x6d\x4b\xb0\x13\x4d\x4a\xcd\x38\xdd\xe7\x0d\x91\xe2\xc4\x8b\xe4\x4a\x7c\x15\x8c\x7f\x55\x8f\x4c\x51\x59\x33\x0d\x12\x4e\xf9\x45\xde\x56\xb9\x94\xf9\x85\x70\xc1\xe9\x24\xcf\xee\xec\xa1\xf4\xb1\x9a\x5c\x55\x36\x02\x28\x32\xf4\x1f\xe2\x55\x10\xc3\x3f\x08\x36\x33\x37\x9a\xf1\x84\x18\x3e\x00\x0d\x19\xc3\x44\x6e\x3c\xbf\x9c\x94\x4a\x10\xb8\x3f\x74\xc0\x7e\x5f\xcb\xd0\x37\x2d\x82\x99\xa3\xbe\x79\xba\x09\x5e\xe1\xd0\x14\xc3\x2b\x73\x0c\x1c\x8d\xc0\x58\xd3\xf7\x26\x1a\xd9\x8f\x46\x58\x35\xf0\x26\xda\x11\x84\x23\x70\xf8\x94\x27\x5b\xc1\x54\xd2\xde\xb6\xc6\x4d\x38\xd0\x78\x7d\x90\x36\x6c\xd7\xf4\x63\x27\xea\x13\xe7\x08\x6f\xff\x60\x0a\x6e\x6f\x90\xbd\x34\x8f\xbc\x4e\xcd\x02\xdf\x89\xfa\x3f\x08\xcf\x51\x6f\xe0\xf7\xf1\xdf\x1b\x9a\x7d\x34\xd7\xb7\xf2\xdb\x73\xb8\xef\x32\x75\xcb\xdd\x22\x35\xaf\x76\xb7\xf8\x0c\x00\x00\xff\xff\xed\x96\xaa\xe2\xde\x03\x00\x00")
func piecesWrSvgBytes() ([]byte, error) {
return bindataRead(
_piecesWrSvg,
"pieces/wR.svg",
)
}
func piecesWrSvg() (*asset, error) {
bytes, err := piecesWrSvgBytes()
if err != nil {
return nil, err
}
info := bindataFileInfo{name: "pieces/wR.svg", size: 990, mode: os.FileMode(420), modTime: time.Unix(1445797380, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
// Asset loads and returns the asset for the given name.
// It returns an error if the asset could not be found or
// could not be loaded.
func Asset(name string) ([]byte, error) {
cannonicalName := strings.Replace(name, "\\", "/", -1)
if f, ok := _bindata[cannonicalName]; ok {
a, err := f()
if err != nil {
return nil, fmt.Errorf("Asset %s can't read by error: %v", name, err)
}
return a.bytes, nil
}
return nil, fmt.Errorf("Asset %s not found", name)
}
// MustAsset is like Asset but panics when Asset would return an error.
// It simplifies safe initialization of global variables.
func MustAsset(name string) []byte {
a, err := Asset(name)
if err != nil {
panic("asset: Asset(" + name + "): " + err.Error())
}
return a
}
// AssetInfo loads and returns the asset info for the given name.
// It returns an error if the asset could not be found or
// could not be loaded.
func AssetInfo(name string) (os.FileInfo, error) {
cannonicalName := strings.Replace(name, "\\", "/", -1)
if f, ok := _bindata[cannonicalName]; ok {
a, err := f()
if err != nil {
return nil, fmt.Errorf("AssetInfo %s can't read by error: %v", name, err)
}
return a.info, nil
}
return nil, fmt.Errorf("AssetInfo %s not found", name)
}
// AssetNames returns the names of the assets.
func AssetNames() []string {
names := make([]string, 0, len(_bindata))
for name := range _bindata {
names = append(names, name)
}
return names
}
// _bindata is a table, holding each asset generator, mapped to its name.
var _bindata = map[string]func() (*asset, error){
"pieces/.DS_Store": piecesDs_store,
"pieces/bB.svg": piecesBbSvg,
"pieces/bK.svg": piecesBkSvg,
"pieces/bN.svg": piecesBnSvg,
"pieces/bP.svg": piecesBpSvg,
"pieces/bQ.svg": piecesBqSvg,
"pieces/bR.svg": piecesBrSvg,
"pieces/wB.svg": piecesWbSvg,
"pieces/wK.svg": piecesWkSvg,
"pieces/wN.svg": piecesWnSvg,
"pieces/wP.svg": piecesWpSvg,
"pieces/wQ.svg": piecesWqSvg,
"pieces/wR.svg": piecesWrSvg,
}
// AssetDir returns the file names below a certain
// directory embedded in the file by go-bindata.
// For example if you run go-bindata on data/... and data contains the
// following hierarchy:
// data/
// foo.txt
// img/
// a.png
// b.png
// then AssetDir("data") would return []string{"foo.txt", "img"}
// AssetDir("data/img") would return []string{"a.png", "b.png"}
// AssetDir("foo.txt") and AssetDir("notexist") would return an error
// AssetDir("") will return []string{"data"}.
func AssetDir(name string) ([]string, error) {
node := _bintree
if len(name) != 0 {
cannonicalName := strings.Replace(name, "\\", "/", -1)
pathList := strings.Split(cannonicalName, "/")
for _, p := range pathList {
node = node.Children[p]
if node == nil {
return nil, fmt.Errorf("Asset %s not found", name)
}
}
}
if node.Func != nil {
return nil, fmt.Errorf("Asset %s not found", name)
}
rv := make([]string, 0, len(node.Children))
for childName := range node.Children {
rv = append(rv, childName)
}
return rv, nil
}
type bintree struct {
Func func() (*asset, error)
Children map[string]*bintree
}
var _bintree = &bintree{nil, map[string]*bintree{
"pieces": &bintree{nil, map[string]*bintree{
".DS_Store": &bintree{piecesDs_store, map[string]*bintree{}},
"bB.svg": &bintree{piecesBbSvg, map[string]*bintree{}},
"bK.svg": &bintree{piecesBkSvg, map[string]*bintree{}},
"bN.svg": &bintree{piecesBnSvg, map[string]*bintree{}},
"bP.svg": &bintree{piecesBpSvg, map[string]*bintree{}},
"bQ.svg": &bintree{piecesBqSvg, map[string]*bintree{}},
"bR.svg": &bintree{piecesBrSvg, map[string]*bintree{}},
"wB.svg": &bintree{piecesWbSvg, map[string]*bintree{}},
"wK.svg": &bintree{piecesWkSvg, map[string]*bintree{}},
"wN.svg": &bintree{piecesWnSvg, map[string]*bintree{}},
"wP.svg": &bintree{piecesWpSvg, map[string]*bintree{}},
"wQ.svg": &bintree{piecesWqSvg, map[string]*bintree{}},
"wR.svg": &bintree{piecesWrSvg, map[string]*bintree{}},
}},
}}
// RestoreAsset restores an asset under the given directory
func RestoreAsset(dir, name string) error {
data, err := Asset(name)
if err != nil {
return err
}
info, err := AssetInfo(name)
if err != nil {
return err
}
err = os.MkdirAll(_filePath(dir, filepath.Dir(name)), os.FileMode(0755))
if err != nil {
return err
}
err = ioutil.WriteFile(_filePath(dir, name), data, info.Mode())
if err != nil {
return err
}
err = os.Chtimes(_filePath(dir, name), info.ModTime(), info.ModTime())
if err != nil {
return err
}
return nil
}
// RestoreAssets restores an asset under the given directory recursively
func RestoreAssets(dir, name string) error {
children, err := AssetDir(name)
// File
if err != nil {
return RestoreAsset(dir, name)
}
// Dir
for _, child := range children {
err = RestoreAssets(dir, filepath.Join(name, child))
if err != nil {
return err
}
}
return nil
}
func _filePath(dir, name string) string {
cannonicalName := strings.Replace(name, "\\", "/", -1)
return filepath.Join(append([]string{dir}, strings.Split(cannonicalName, "/")...)...)
}

15
image/internal/pieces/bB.svg

@ -0,0 +1,15 @@ @@ -0,0 +1,15 @@
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="45" height="45">
<g style="opacity:1; fill:none; fill-rule:evenodd; fill-opacity:1; stroke:#000000; stroke-width:1.5; stroke-linecap:round; stroke-linejoin:round; stroke-miterlimit:4; stroke-dasharray:none; stroke-opacity:1;">
<g style="fill:#000000; stroke:#000000; stroke-linecap:butt;">
<path
d="M 9,36 C 12.39,35.03 19.11,36.43 22.5,34 C 25.89,36.43 32.61,35.03 36,36 C 36,36 37.65,36.54 39,38 C 38.32,38.97 37.35,38.99 36,38.5 C 32.61,37.53 25.89,38.96 22.5,37.5 C 19.11,38.96 12.39,37.53 9,38.5 C 7.646,38.99 6.677,38.97 6,38 C 7.354,36.06 9,36 9,36 z" />
<path
d="M 15,32 C 17.5,34.5 27.5,34.5 30,32 C 30.5,30.5 30,30 30,30 C 30,27.5 27.5,26 27.5,26 C 33,24.5 33.5,14.5 22.5,10.5 C 11.5,14.5 12,24.5 17.5,26 C 17.5,26 15,27.5 15,30 C 15,30 14.5,30.5 15,32 z" />
<path
d="M 25 8 A 2.5 2.5 0 1 1 20,8 A 2.5 2.5 0 1 1 25 8 z" />
</g>
<path
d="M 17.5,26 L 27.5,26 M 15,30 L 30,30 M 22.5,15.5 L 22.5,20.5 M 20,18 L 25,18"
style="fill:none; stroke:#ffffff; stroke-linejoin:miter;" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

23
image/internal/pieces/bK.svg

@ -0,0 +1,23 @@ @@ -0,0 +1,23 @@
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="45" height="45">
<g style="fill:none; fill-opacity:1; fill-rule:evenodd; stroke:#000000; stroke-width:1.5; stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4; stroke-dasharray:none; stroke-opacity:1;">
<path
d="M 22.5,11.63 L 22.5,6"
style="fill:none; stroke:#000000; stroke-linejoin:miter;"
id="path6570" />
<path
d="M 22.5,25 C 22.5,25 27,17.5 25.5,14.5 C 25.5,14.5 24.5,12 22.5,12 C 20.5,12 19.5,14.5 19.5,14.5 C 18,17.5 22.5,25 22.5,25"
style="fill:#000000;fill-opacity:1; stroke-linecap:butt; stroke-linejoin:miter;" />
<path
d="M 11.5,37 C 17,40.5 27,40.5 32.5,37 L 32.5,30 C 32.5,30 41.5,25.5 38.5,19.5 C 34.5,13 25,16 22.5,23.5 L 22.5,27 L 22.5,23.5 C 19,16 9.5,13 6.5,19.5 C 3.5,25.5 11.5,29.5 11.5,29.5 L 11.5,37 z "
style="fill:#000000; stroke:#000000;" />
<path
d="M 20,8 L 25,8"
style="fill:none; stroke:#000000; stroke-linejoin:miter;" />
<path
d="M 32,29.5 C 32,29.5 40.5,25.5 38.03,19.85 C 34.15,14 25,18 22.5,24.5 L 22.51,26.6 L 22.5,24.5 C 20,18 9.906,14 6.997,19.85 C 4.5,25.5 11.85,28.85 11.85,28.85"
style="fill:none; stroke:#ffffff;" />
<path
d="M 11.5,30 C 17,27 27,27 32.5,30 M 11.5,33.5 C 17,30.5 27,30.5 32.5,33.5 M 11.5,37 C 17,34 27,34 32.5,37"
style="fill:none; stroke:#ffffff;" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

20
image/internal/pieces/bN.svg

@ -0,0 +1,20 @@ @@ -0,0 +1,20 @@
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="45" height="45">
<g style="opacity:1; fill:none; fill-opacity:1; fill-rule:evenodd; stroke:#000000; stroke-width:1.5; stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4; stroke-dasharray:none; stroke-opacity:1;">
<path
d="M 22,10 C 32.5,11 38.5,18 38,39 L 15,39 C 15,30 25,32.5 23,18"
style="fill:#000000; stroke:#000000;" />
<path
d="M 24,18 C 24.38,20.91 18.45,25.37 16,27 C 13,29 13.18,31.34 11,31 C 9.958,30.06 12.41,27.96 11,28 C 10,28 11.19,29.23 10,30 C 9,30 5.997,31 6,26 C 6,24 12,14 12,14 C 12,14 13.89,12.1 14,10.5 C 13.27,9.506 13.5,8.5 13.5,7.5 C 14.5,6.5 16.5,10 16.5,10 L 18.5,10 C 18.5,10 19.28,8.008 21,7 C 22,7 22,10 22,10"
style="fill:#000000; stroke:#000000;" />
<path
d="M 9.5 25.5 A 0.5 0.5 0 1 1 8.5,25.5 A 0.5 0.5 0 1 1 9.5 25.5 z"
style="fill:#ffffff; stroke:#ffffff;" />
<path
d="M 15 15.5 A 0.5 1.5 0 1 1 14,15.5 A 0.5 1.5 0 1 1 15 15.5 z"
transform="matrix(0.866,0.5,-0.5,0.866,9.693,-5.173)"
style="fill:#ffffff; stroke:#ffffff;" />
<path
d="M 24.55,10.4 L 24.1,11.85 L 24.6,12 C 27.75,13 30.25,14.49 32.5,18.75 C 34.75,23.01 35.75,29.06 35.25,39 L 35.2,39.5 L 37.45,39.5 L 37.5,39 C 38,28.94 36.62,22.15 34.25,17.66 C 31.88,13.17 28.46,11.02 25.06,10.5 L 24.55,10.4 z "
style="fill:#ffffff; stroke:none;" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

5
image/internal/pieces/bP.svg

@ -0,0 +1,5 @@ @@ -0,0 +1,5 @@
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="45" height="45">
<path
d="M 22,9 C 19.79,9 18,10.79 18,13 C 18,13.89 18.29,14.71 18.78,15.38 C 16.83,16.5 15.5,18.59 15.5,21 C 15.5,23.03 16.44,24.84 17.91,26.03 C 14.91,27.09 10.5,31.58 10.5,39.5 L 33.5,39.5 C 33.5,31.58 29.09,27.09 26.09,26.03 C 27.56,24.84 28.5,23.03 28.5,21 C 28.5,18.59 27.17,16.5 25.22,15.38 C 25.71,14.71 26,13.89 26,13 C 26,10.79 24.21,9 22,9 z "
style="opacity:1; fill:#000000; fill-opacity:1; fill-rule:nonzero; stroke:#000000; stroke-width:1.5; stroke-linecap:round; stroke-linejoin:miter; stroke-miterlimit:4; stroke-dasharray:none; stroke-opacity:1;" />
</svg>

After

Width:  |  Height:  |  Size: 662 B

32
image/internal/pieces/bQ.svg

@ -0,0 +1,32 @@ @@ -0,0 +1,32 @@
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="45" height="45">
<g style="opacity:1; fill:000000; fill-opacity:1; fill-rule:evenodd; stroke:#000000; stroke-width:1.5; stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4; stroke-dasharray:none; stroke-opacity:1;">
<g style="fill:#000000; stroke:none;">
<circle cx="6" cy="12" r="2.75" />
<circle cx="14" cy="9" r="2.75" />
<circle cx="22.5" cy="8" r="2.75" />
<circle cx="31" cy="9" r="2.75" />
<circle cx="39" cy="12" r="2.75" />
</g>
<path
d="M 9,26 C 17.5,24.5 30,24.5 36,26 L 38.5,13.5 L 31,25 L 30.7,10.9 L 25.5,24.5 L 22.5,10 L 19.5,24.5 L 14.3,10.9 L 14,25 L 6.5,13.5 L 9,26 z"
style="stroke-linecap:butt; stroke:#000000;" />
<path
d="M 9,26 C 9,28 10.5,28 11.5,30 C 12.5,31.5 12.5,31 12,33.5 C 10.5,34.5 10.5,36 10.5,36 C 9,37.5 11,38.5 11,38.5 C 17.5,39.5 27.5,39.5 34,38.5 C 34,38.5 35.5,37.5 34,36 C 34,36 34.5,34.5 33,33.5 C 32.5,31 32.5,31.5 33.5,30 C 34.5,28 36,28 36,26 C 27.5,24.5 17.5,24.5 9,26 z"
style="stroke-linecap:butt;" />
<path
d="M 11,38.5 A 35,35 1 0 0 34,38.5"
style="fill:none; stroke:#000000; stroke-linecap:butt;" />
<path
d="M 11,29 A 35,35 1 0 1 34,29"
style="fill:none; stroke:#ffffff;" />
<path
d="M 12.5,31.5 L 32.5,31.5"
style="fill:none; stroke:#ffffff;" />
<path
d="M 11.5,34.5 A 35,35 1 0 0 33.5,34.5"
style="fill:none; stroke:#ffffff;" />
<path
d="M 10.5,37.5 A 35,35 1 0 0 34.5,37.5"
style="fill:none; stroke:#ffffff;" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

37
image/internal/pieces/bR.svg

@ -0,0 +1,37 @@ @@ -0,0 +1,37 @@
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="45" height="45">
<g style="opacity:1; fill:000000; fill-opacity:1; fill-rule:evenodd; stroke:#000000; stroke-width:1.5; stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4; stroke-dasharray:none; stroke-opacity:1;">
<path
d="M 9,39 L 36,39 L 36,36 L 9,36 L 9,39 z "
style="stroke-linecap:butt;" />
<path
d="M 12.5,32 L 14,29.5 L 31,29.5 L 32.5,32 L 12.5,32 z "
style="stroke-linecap:butt;" />
<path
d="M 12,36 L 12,32 L 33,32 L 33,36 L 12,36 z "
style="stroke-linecap:butt;" />
<path
d="M 14,29.5 L 14,16.5 L 31,16.5 L 31,29.5 L 14,29.5 z "
style="stroke-linecap:butt;stroke-linejoin:miter;" />
<path
d="M 14,16.5 L 11,14 L 34,14 L 31,16.5 L 14,16.5 z "
style="stroke-linecap:butt;" />
<path
d="M 11,14 L 11,9 L 15,9 L 15,11 L 20,11 L 20,9 L 25,9 L 25,11 L 30,11 L 30,9 L 34,9 L 34,14 L 11,14 z "
style="stroke-linecap:butt;" />
<path
d="M 12,35.5 L 33,35.5 L 33,35.5"
style="fill:none; stroke:#ffffff; stroke-width:1; stroke-linejoin:miter;" />
<path
d="M 13,31.5 L 32,31.5"
style="fill:none; stroke:#ffffff; stroke-width:1; stroke-linejoin:miter;" />
<path
d="M 14,29.5 L 31,29.5"
style="fill:none; stroke:#ffffff; stroke-width:1; stroke-linejoin:miter;" />
<path
d="M 14,16.5 L 31,16.5"
style="fill:none; stroke:#ffffff; stroke-width:1; stroke-linejoin:miter;" />
<path
d="M 11,14 L 34,14"
style="fill:none; stroke:#ffffff; stroke-width:1; stroke-linejoin:miter;" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

15
image/internal/pieces/wB.svg

@ -0,0 +1,15 @@ @@ -0,0 +1,15 @@
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="45" height="45">
<g style="opacity:1; fill:none; fill-rule:evenodd; fill-opacity:1; stroke:#000000; stroke-width:1.5; stroke-linecap:round; stroke-linejoin:round; stroke-miterlimit:4; stroke-dasharray:none; stroke-opacity:1;">
<g style="fill:#ffffff; stroke:#000000; stroke-linecap:butt;">
<path
d="M 9,36 C 12.39,35.03 19.11,36.43 22.5,34 C 25.89,36.43 32.61,35.03 36,36 C 36,36 37.65,36.54 39,38 C 38.32,38.97 37.35,38.99 36,38.5 C 32.61,37.53 25.89,38.96 22.5,37.5 C 19.11,38.96 12.39,37.53 9,38.5 C 7.646,38.99 6.677,38.97 6,38 C 7.354,36.06 9,36 9,36 z" />
<path
d="M 15,32 C 17.5,34.5 27.5,34.5 30,32 C 30.5,30.5 30,30 30,30 C 30,27.5 27.5,26 27.5,26 C 33,24.5 33.5,14.5 22.5,10.5 C 11.5,14.5 12,24.5 17.5,26 C 17.5,26 15,27.5 15,30 C 15,30 14.5,30.5 15,32 z" />
<path
d="M 25 8 A 2.5 2.5 0 1 1 20,8 A 2.5 2.5 0 1 1 25 8 z" />
</g>
<path
d="M 17.5,26 L 27.5,26 M 15,30 L 30,30 M 22.5,15.5 L 22.5,20.5 M 20,18 L 25,18"
style="fill:none; stroke:#000000; stroke-linejoin:miter;" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

25
image/internal/pieces/wK.svg

@ -0,0 +1,25 @@ @@ -0,0 +1,25 @@
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="45" height="45">
<g style="fill:none; fill-opacity:1; fill-rule:evenodd; stroke:#000000; stroke-width:1.5; stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4; stroke-dasharray:none; stroke-opacity:1;">
<path
d="M 22.5,11.63 L 22.5,6"
style="fill:none; stroke:#000000; stroke-linejoin:miter;" />
<path
d="M 20,8 L 25,8"
style="fill:none; stroke:#000000; stroke-linejoin:miter;" />
<path
d="M 22.5,25 C 22.5,25 27,17.5 25.5,14.5 C 25.5,14.5 24.5,12 22.5,12 C 20.5,12 19.5,14.5 19.5,14.5 C 18,17.5 22.5,25 22.5,25"
style="fill:#ffffff; stroke:#000000; stroke-linecap:butt; stroke-linejoin:miter;" />
<path
d="M 11.5,37 C 17,40.5 27,40.5 32.5,37 L 32.5,30 C 32.5,30 41.5,25.5 38.5,19.5 C 34.5,13 25,16 22.5,23.5 L 22.5,27 L 22.5,23.5 C 19,16 9.5,13 6.5,19.5 C 3.5,25.5 11.5,29.5 11.5,29.5 L 11.5,37 z "
style="fill:#ffffff; stroke:#000000;" />
<path
d="M 11.5,30 C 17,27 27,27 32.5,30"
style="fill:none; stroke:#000000;" />
<path
d="M 11.5,33.5 C 17,30.5 27,30.5 32.5,33.5"
style="fill:none; stroke:#000000;" />
<path
d="M 11.5,37 C 17,34 27,34 32.5,37"
style="fill:none; stroke:#000000;" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

17
image/internal/pieces/wN.svg

@ -0,0 +1,17 @@ @@ -0,0 +1,17 @@
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="45" height="45">
<g style="opacity:1; fill:none; fill-opacity:1; fill-rule:evenodd; stroke:#000000; stroke-width:1.5; stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4; stroke-dasharray:none; stroke-opacity:1;">
<path
d="M 22,10 C 32.5,11 38.5,18 38,39 L 15,39 C 15,30 25,32.5 23,18"
style="fill:#ffffff; stroke:#000000;" />
<path
d="M 24,18 C 24.38,20.91 18.45,25.37 16,27 C 13,29 13.18,31.34 11,31 C 9.958,30.06 12.41,27.96 11,28 C 10,28 11.19,29.23 10,30 C 9,30 5.997,31 6,26 C 6,24 12,14 12,14 C 12,14 13.89,12.1 14,10.5 C 13.27,9.506 13.5,8.5 13.5,7.5 C 14.5,6.5 16.5,10 16.5,10 L 18.5,10 C 18.5,10 19.28,8.008 21,7 C 22,7 22,10 22,10"
style="fill:#ffffff; stroke:#000000;" />
<path
d="M 9.5 25.5 A 0.5 0.5 0 1 1 8.5,25.5 A 0.5 0.5 0 1 1 9.5 25.5 z"
style="fill:#000000; stroke:#000000;" />
<path
d="M 15 15.5 A 0.5 1.5 0 1 1 14,15.5 A 0.5 1.5 0 1 1 15 15.5 z"
transform="matrix(0.866,0.5,-0.5,0.866,9.693,-5.173)"
style="fill:#000000; stroke:#000000;" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

5
image/internal/pieces/wP.svg

@ -0,0 +1,5 @@ @@ -0,0 +1,5 @@
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="45" height="45">
<path
d="M 22,9 C 19.79,9 18,10.79 18,13 C 18,13.89 18.29,14.71 18.78,15.38 C 16.83,16.5 15.5,18.59 15.5,21 C 15.5,23.03 16.44,24.84 17.91,26.03 C 14.91,27.09 10.5,31.58 10.5,39.5 L 33.5,39.5 C 33.5,31.58 29.09,27.09 26.09,26.03 C 27.56,24.84 28.5,23.03 28.5,21 C 28.5,18.59 27.17,16.5 25.22,15.38 C 25.71,14.71 26,13.89 26,13 C 26,10.79 24.21,9 22,9 z "
style="opacity:1; fill:#ffffff; fill-opacity:1; fill-rule:nonzero; stroke:#000000; stroke-width:1.5; stroke-linecap:round; stroke-linejoin:miter; stroke-miterlimit:4; stroke-dasharray:none; stroke-opacity:1;" />
</svg>

After

Width:  |  Height:  |  Size: 662 B

31
image/internal/pieces/wQ.svg

@ -0,0 +1,31 @@ @@ -0,0 +1,31 @@
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="45" height="45">
<g style="opacity:1; fill:#ffffff; fill-opacity:1; fill-rule:evenodd; stroke:#000000; stroke-width:1.5; stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4; stroke-dasharray:none; stroke-opacity:1;">
<path
d="M 9 13 A 2 2 0 1 1 5,13 A 2 2 0 1 1 9 13 z"
transform="translate(-1,-1)" />
<path
d="M 9 13 A 2 2 0 1 1 5,13 A 2 2 0 1 1 9 13 z"
transform="translate(15.5,-5.5)" />
<path
d="M 9 13 A 2 2 0 1 1 5,13 A 2 2 0 1 1 9 13 z"
transform="translate(32,-1)" />
<path
d="M 9 13 A 2 2 0 1 1 5,13 A 2 2 0 1 1 9 13 z"
transform="translate(7,-4.5)" />
<path
d="M 9 13 A 2 2 0 1 1 5,13 A 2 2 0 1 1 9 13 z"
transform="translate(24,-4)" />
<path
d="M 9,26 C 17.5,24.5 30,24.5 36,26 L 38,14 L 31,25 L 31,11 L 25.5,24.5 L 22.5,9.5 L 19.5,24.5 L 14,10.5 L 14,25 L 7,14 L 9,26 z "
style="stroke-linecap:butt;" />
<path
d="M 9,26 C 9,28 10.5,28 11.5,30 C 12.5,31.5 12.5,31 12,33.5 C 10.5,34.5 10.5,36 10.5,36 C 9,37.5 11,38.5 11,38.5 C 17.5,39.5 27.5,39.5 34,38.5 C 34,38.5 35.5,37.5 34,36 C 34,36 34.5,34.5 33,33.5 C 32.5,31 32.5,31.5 33.5,30 C 34.5,28 36,28 36,26 C 27.5,24.5 17.5,24.5 9,26 z "
style="stroke-linecap:butt;" />
<path
d="M 11.5,30 C 15,29 30,29 33.5,30"
style="fill:none;" />
<path
d="M 12,33.5 C 18,32.5 27,32.5 33,33.5"
style="fill:none;" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

23
image/internal/pieces/wR.svg

@ -0,0 +1,23 @@ @@ -0,0 +1,23 @@
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="45" height="45">
<g style="opacity:1; fill:#ffffff; fill-opacity:1; fill-rule:evenodd; stroke:#000000; stroke-width:1.5; stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4; stroke-dasharray:none; stroke-opacity:1;">
<path
d="M 9,39 L 36,39 L 36,36 L 9,36 L 9,39 z "
style="stroke-linecap:butt;" />
<path
d="M 12,36 L 12,32 L 33,32 L 33,36 L 12,36 z "
style="stroke-linecap:butt;" />
<path
d="M 11,14 L 11,9 L 15,9 L 15,11 L 20,11 L 20,9 L 25,9 L 25,11 L 30,11 L 30,9 L 34,9 L 34,14"
style="stroke-linecap:butt;" />
<path
d="M 34,14 L 31,17 L 14,17 L 11,14" />
<path
d="M 31,17 L 31,29.5 L 14,29.5 L 14,17"
style="stroke-linecap:butt; stroke-linejoin:miter;" />
<path
d="M 31,29.5 L 32.5,32 L 12.5,32 L 14,29.5" />
<path
d="M 11,14 L 34,14"
style="fill:none; stroke:#000000; stroke-linejoin:miter;" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 990 B

534
image/test.svg

@ -0,0 +1,534 @@ @@ -0,0 +1,534 @@
<?xml version="1.0"?>
<!-- Generated by SVGo -->
<svg width="360" height="360"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink">
<rect x="0" y="0" width="360" height="360" />
<rect x="0" y="315" width="45" height="45" style="fill: #a57551"/>
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="360" height="360" viewBox="0 -315 360 360">
<g style="opacity:1; fill:#ffffff; fill-opacity:1; fill-rule:evenodd; stroke:#000000; stroke-width:1.5; stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4; stroke-dasharray:none; stroke-opacity:1;">
<path
d="M 9,39 L 36,39 L 36,36 L 9,36 L 9,39 z "
style="stroke-linecap:butt;" />
<path
d="M 12,36 L 12,32 L 33,32 L 33,36 L 12,36 z "
style="stroke-linecap:butt;" />
<path
d="M 11,14 L 11,9 L 15,9 L 15,11 L 20,11 L 20,9 L 25,9 L 25,11 L 30,11 L 30,9 L 34,9 L 34,14"
style="stroke-linecap:butt;" />
<path
d="M 34,14 L 31,17 L 14,17 L 11,14" />
<path
d="M 31,17 L 31,29.5 L 14,29.5 L 14,17"
style="stroke-linecap:butt; stroke-linejoin:miter;" />
<path
d="M 31,29.5 L 32.5,32 L 12.5,32 L 14,29.5" />
<path
d="M 11,14 L 34,14"
style="fill:none; stroke:#000000; stroke-linejoin:miter;" />
</g>
</svg>
<text x="2" y="326" style="font-size:11px;fill: #ebd1a6">1</text>
<text x="42" y="357" style="text-anchor:end;font-size:11px;fill: #ebd1a6">a</text>
<rect x="45" y="315" width="45" height="45" style="fill: #ebd1a6"/>
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="360" height="360" viewBox="-45 -315 360 360">
<g style="opacity:1; fill:none; fill-opacity:1; fill-rule:evenodd; stroke:#000000; stroke-width:1.5; stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4; stroke-dasharray:none; stroke-opacity:1;">
<path
d="M 22,10 C 32.5,11 38.5,18 38,39 L 15,39 C 15,30 25,32.5 23,18"
style="fill:#ffffff; stroke:#000000;" />
<path
d="M 24,18 C 24.38,20.91 18.45,25.37 16,27 C 13,29 13.18,31.34 11,31 C 9.958,30.06 12.41,27.96 11,28 C 10,28 11.19,29.23 10,30 C 9,30 5.997,31 6,26 C 6,24 12,14 12,14 C 12,14 13.89,12.1 14,10.5 C 13.27,9.506 13.5,8.5 13.5,7.5 C 14.5,6.5 16.5,10 16.5,10 L 18.5,10 C 18.5,10 19.28,8.008 21,7 C 22,7 22,10 22,10"
style="fill:#ffffff; stroke:#000000;" />
<path
d="M 9.5 25.5 A 0.5 0.5 0 1 1 8.5,25.5 A 0.5 0.5 0 1 1 9.5 25.5 z"
style="fill:#000000; stroke:#000000;" />
<path
d="M 15 15.5 A 0.5 1.5 0 1 1 14,15.5 A 0.5 1.5 0 1 1 15 15.5 z"
transform="matrix(0.866,0.5,-0.5,0.866,9.693,-5.173)"
style="fill:#000000; stroke:#000000;" />
</g>
</svg>
<text x="87" y="357" style="text-anchor:end;font-size:11px;fill: #a57551">b</text>
<rect x="90" y="315" width="45" height="45" style="fill: #a57551"/>
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="360" height="360" viewBox="-90 -315 360 360">
<g style="opacity:1; fill:none; fill-rule:evenodd; fill-opacity:1; stroke:#000000; stroke-width:1.5; stroke-linecap:round; stroke-linejoin:round; stroke-miterlimit:4; stroke-dasharray:none; stroke-opacity:1;">
<g style="fill:#ffffff; stroke:#000000; stroke-linecap:butt;">
<path
d="M 9,36 C 12.39,35.03 19.11,36.43 22.5,34 C 25.89,36.43 32.61,35.03 36,36 C 36,36 37.65,36.54 39,38 C 38.32,38.97 37.35,38.99 36,38.5 C 32.61,37.53 25.89,38.96 22.5,37.5 C 19.11,38.96 12.39,37.53 9,38.5 C 7.646,38.99 6.677,38.97 6,38 C 7.354,36.06 9,36 9,36 z" />
<path
d="M 15,32 C 17.5,34.5 27.5,34.5 30,32 C 30.5,30.5 30,30 30,30 C 30,27.5 27.5,26 27.5,26 C 33,24.5 33.5,14.5 22.5,10.5 C 11.5,14.5 12,24.5 17.5,26 C 17.5,26 15,27.5 15,30 C 15,30 14.5,30.5 15,32 z" />
<path
d="M 25 8 A 2.5 2.5 0 1 1 20,8 A 2.5 2.5 0 1 1 25 8 z" />
</g>
<path
d="M 17.5,26 L 27.5,26 M 15,30 L 30,30 M 22.5,15.5 L 22.5,20.5 M 20,18 L 25,18"
style="fill:none; stroke:#000000; stroke-linejoin:miter;" />
</g>
</svg>
<text x="132" y="357" style="text-anchor:end;font-size:11px;fill: #ebd1a6">c</text>
<rect x="135" y="315" width="45" height="45" style="fill: #ebd1a6"/>
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="360" height="360" viewBox="-135 -315 360 360">
<g style="opacity:1; fill:#ffffff; fill-opacity:1; fill-rule:evenodd; stroke:#000000; stroke-width:1.5; stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4; stroke-dasharray:none; stroke-opacity:1;">
<path
d="M 9 13 A 2 2 0 1 1 5,13 A 2 2 0 1 1 9 13 z"
transform="translate(-1,-1)" />
<path
d="M 9 13 A 2 2 0 1 1 5,13 A 2 2 0 1 1 9 13 z"
transform="translate(15.5,-5.5)" />
<path
d="M 9 13 A 2 2 0 1 1 5,13 A 2 2 0 1 1 9 13 z"
transform="translate(32,-1)" />
<path
d="M 9 13 A 2 2 0 1 1 5,13 A 2 2 0 1 1 9 13 z"
transform="translate(7,-4.5)" />
<path
d="M 9 13 A 2 2 0 1 1 5,13 A 2 2 0 1 1 9 13 z"
transform="translate(24,-4)" />
<path
d="M 9,26 C 17.5,24.5 30,24.5 36,26 L 38,14 L 31,25 L 31,11 L 25.5,24.5 L 22.5,9.5 L 19.5,24.5 L 14,10.5 L 14,25 L 7,14 L 9,26 z "
style="stroke-linecap:butt;" />
<path
d="M 9,26 C 9,28 10.5,28 11.5,30 C 12.5,31.5 12.5,31 12,33.5 C 10.5,34.5 10.5,36 10.5,36 C 9,37.5 11,38.5 11,38.5 C 17.5,39.5 27.5,39.5 34,38.5 C 34,38.5 35.5,37.5 34,36 C 34,36 34.5,34.5 33,33.5 C 32.5,31 32.5,31.5 33.5,30 C 34.5,28 36,28 36,26 C 27.5,24.5 17.5,24.5 9,26 z "
style="stroke-linecap:butt;" />
<path
d="M 11.5,30 C 15,29 30,29 33.5,30"
style="fill:none;" />
<path
d="M 12,33.5 C 18,32.5 27,32.5 33,33.5"
style="fill:none;" />
</g>
</svg>
<text x="177" y="357" style="text-anchor:end;font-size:11px;fill: #a57551">d</text>
<rect x="180" y="315" width="45" height="45" style="fill: #a57551"/>
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="360" height="360" viewBox="-180 -315 360 360">
<g style="fill:none; fill-opacity:1; fill-rule:evenodd; stroke:#000000; stroke-width:1.5; stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4; stroke-dasharray:none; stroke-opacity:1;">
<path
d="M 22.5,11.63 L 22.5,6"
style="fill:none; stroke:#000000; stroke-linejoin:miter;" />
<path
d="M 20,8 L 25,8"
style="fill:none; stroke:#000000; stroke-linejoin:miter;" />
<path
d="M 22.5,25 C 22.5,25 27,17.5 25.5,14.5 C 25.5,14.5 24.5,12 22.5,12 C 20.5,12 19.5,14.5 19.5,14.5 C 18,17.5 22.5,25 22.5,25"
style="fill:#ffffff; stroke:#000000; stroke-linecap:butt; stroke-linejoin:miter;" />
<path
d="M 11.5,37 C 17,40.5 27,40.5 32.5,37 L 32.5,30 C 32.5,30 41.5,25.5 38.5,19.5 C 34.5,13 25,16 22.5,23.5 L 22.5,27 L 22.5,23.5 C 19,16 9.5,13 6.5,19.5 C 3.5,25.5 11.5,29.5 11.5,29.5 L 11.5,37 z "
style="fill:#ffffff; stroke:#000000;" />
<path
d="M 11.5,30 C 17,27 27,27 32.5,30"
style="fill:none; stroke:#000000;" />
<path
d="M 11.5,33.5 C 17,30.5 27,30.5 32.5,33.5"
style="fill:none; stroke:#000000;" />
<path
d="M 11.5,37 C 17,34 27,34 32.5,37"
style="fill:none; stroke:#000000;" />
</g>
</svg>
<text x="222" y="357" style="text-anchor:end;font-size:11px;fill: #ebd1a6">e</text>
<rect x="225" y="315" width="45" height="45" style="fill: #ebd1a6"/>
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="360" height="360" viewBox="-225 -315 360 360">
<g style="opacity:1; fill:none; fill-rule:evenodd; fill-opacity:1; stroke:#000000; stroke-width:1.5; stroke-linecap:round; stroke-linejoin:round; stroke-miterlimit:4; stroke-dasharray:none; stroke-opacity:1;">
<g style="fill:#ffffff; stroke:#000000; stroke-linecap:butt;">
<path
d="M 9,36 C 12.39,35.03 19.11,36.43 22.5,34 C 25.89,36.43 32.61,35.03 36,36 C 36,36 37.65,36.54 39,38 C 38.32,38.97 37.35,38.99 36,38.5 C 32.61,37.53 25.89,38.96 22.5,37.5 C 19.11,38.96 12.39,37.53 9,38.5 C 7.646,38.99 6.677,38.97 6,38 C 7.354,36.06 9,36 9,36 z" />
<path
d="M 15,32 C 17.5,34.5 27.5,34.5 30,32 C 30.5,30.5 30,30 30,30 C 30,27.5 27.5,26 27.5,26 C 33,24.5 33.5,14.5 22.5,10.5 C 11.5,14.5 12,24.5 17.5,26 C 17.5,26 15,27.5 15,30 C 15,30 14.5,30.5 15,32 z" />
<path
d="M 25 8 A 2.5 2.5 0 1 1 20,8 A 2.5 2.5 0 1 1 25 8 z" />
</g>
<path
d="M 17.5,26 L 27.5,26 M 15,30 L 30,30 M 22.5,15.5 L 22.5,20.5 M 20,18 L 25,18"
style="fill:none; stroke:#000000; stroke-linejoin:miter;" />
</g>
</svg>
<text x="267" y="357" style="text-anchor:end;font-size:11px;fill: #a57551">f</text>
<rect x="270" y="315" width="45" height="45" style="fill: #a57551"/>
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="360" height="360" viewBox="-270 -315 360 360">
<g style="opacity:1; fill:none; fill-opacity:1; fill-rule:evenodd; stroke:#000000; stroke-width:1.5; stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4; stroke-dasharray:none; stroke-opacity:1;">
<path
d="M 22,10 C 32.5,11 38.5,18 38,39 L 15,39 C 15,30 25,32.5 23,18"
style="fill:#ffffff; stroke:#000000;" />
<path
d="M 24,18 C 24.38,20.91 18.45,25.37 16,27 C 13,29 13.18,31.34 11,31 C 9.958,30.06 12.41,27.96 11,28 C 10,28 11.19,29.23 10,30 C 9,30 5.997,31 6,26 C 6,24 12,14 12,14 C 12,14 13.89,12.1 14,10.5 C 13.27,9.506 13.5,8.5 13.5,7.5 C 14.5,6.5 16.5,10 16.5,10 L 18.5,10 C 18.5,10 19.28,8.008 21,7 C 22,7 22,10 22,10"
style="fill:#ffffff; stroke:#000000;" />
<path
d="M 9.5 25.5 A 0.5 0.5 0 1 1 8.5,25.5 A 0.5 0.5 0 1 1 9.5 25.5 z"
style="fill:#000000; stroke:#000000;" />
<path
d="M 15 15.5 A 0.5 1.5 0 1 1 14,15.5 A 0.5 1.5 0 1 1 15 15.5 z"
transform="matrix(0.866,0.5,-0.5,0.866,9.693,-5.173)"
style="fill:#000000; stroke:#000000;" />
</g>
</svg>
<text x="312" y="357" style="text-anchor:end;font-size:11px;fill: #ebd1a6">g</text>
<rect x="315" y="315" width="45" height="45" style="fill: #ebd1a6"/>
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="360" height="360" viewBox="-315 -315 360 360">
<g style="opacity:1; fill:#ffffff; fill-opacity:1; fill-rule:evenodd; stroke:#000000; stroke-width:1.5; stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4; stroke-dasharray:none; stroke-opacity:1;">
<path
d="M 9,39 L 36,39 L 36,36 L 9,36 L 9,39 z "
style="stroke-linecap:butt;" />
<path
d="M 12,36 L 12,32 L 33,32 L 33,36 L 12,36 z "
style="stroke-linecap:butt;" />
<path
d="M 11,14 L 11,9 L 15,9 L 15,11 L 20,11 L 20,9 L 25,9 L 25,11 L 30,11 L 30,9 L 34,9 L 34,14"
style="stroke-linecap:butt;" />
<path
d="M 34,14 L 31,17 L 14,17 L 11,14" />
<path
d="M 31,17 L 31,29.5 L 14,29.5 L 14,17"
style="stroke-linecap:butt; stroke-linejoin:miter;" />
<path
d="M 31,29.5 L 32.5,32 L 12.5,32 L 14,29.5" />
<path
d="M 11,14 L 34,14"
style="fill:none; stroke:#000000; stroke-linejoin:miter;" />
</g>
</svg>
<text x="357" y="357" style="text-anchor:end;font-size:11px;fill: #a57551">h</text>
<rect x="0" y="270" width="45" height="45" style="fill: #ebd1a6"/>
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="360" height="360" viewBox="0 -270 360 360">
<path
d="M 22,9 C 19.79,9 18,10.79 18,13 C 18,13.89 18.29,14.71 18.78,15.38 C 16.83,16.5 15.5,18.59 15.5,21 C 15.5,23.03 16.44,24.84 17.91,26.03 C 14.91,27.09 10.5,31.58 10.5,39.5 L 33.5,39.5 C 33.5,31.58 29.09,27.09 26.09,26.03 C 27.56,24.84 28.5,23.03 28.5,21 C 28.5,18.59 27.17,16.5 25.22,15.38 C 25.71,14.71 26,13.89 26,13 C 26,10.79 24.21,9 22,9 z "
style="opacity:1; fill:#ffffff; fill-opacity:1; fill-rule:nonzero; stroke:#000000; stroke-width:1.5; stroke-linecap:round; stroke-linejoin:miter; stroke-miterlimit:4; stroke-dasharray:none; stroke-opacity:1;" />
</svg>
<text x="2" y="281" style="font-size:11px;fill: #a57551">2</text>
<rect x="45" y="270" width="45" height="45" style="fill: #a57551"/>
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="360" height="360" viewBox="-45 -270 360 360">
<path
d="M 22,9 C 19.79,9 18,10.79 18,13 C 18,13.89 18.29,14.71 18.78,15.38 C 16.83,16.5 15.5,18.59 15.5,21 C 15.5,23.03 16.44,24.84 17.91,26.03 C 14.91,27.09 10.5,31.58 10.5,39.5 L 33.5,39.5 C 33.5,31.58 29.09,27.09 26.09,26.03 C 27.56,24.84 28.5,23.03 28.5,21 C 28.5,18.59 27.17,16.5 25.22,15.38 C 25.71,14.71 26,13.89 26,13 C 26,10.79 24.21,9 22,9 z "
style="opacity:1; fill:#ffffff; fill-opacity:1; fill-rule:nonzero; stroke:#000000; stroke-width:1.5; stroke-linecap:round; stroke-linejoin:miter; stroke-miterlimit:4; stroke-dasharray:none; stroke-opacity:1;" />
</svg>
<rect x="90" y="270" width="45" height="45" style="fill: #ebd1a6"/>
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="360" height="360" viewBox="-90 -270 360 360">
<path
d="M 22,9 C 19.79,9 18,10.79 18,13 C 18,13.89 18.29,14.71 18.78,15.38 C 16.83,16.5 15.5,18.59 15.5,21 C 15.5,23.03 16.44,24.84 17.91,26.03 C 14.91,27.09 10.5,31.58 10.5,39.5 L 33.5,39.5 C 33.5,31.58 29.09,27.09 26.09,26.03 C 27.56,24.84 28.5,23.03 28.5,21 C 28.5,18.59 27.17,16.5 25.22,15.38 C 25.71,14.71 26,13.89 26,13 C 26,10.79 24.21,9 22,9 z "
style="opacity:1; fill:#ffffff; fill-opacity:1; fill-rule:nonzero; stroke:#000000; stroke-width:1.5; stroke-linecap:round; stroke-linejoin:miter; stroke-miterlimit:4; stroke-dasharray:none; stroke-opacity:1;" />
</svg>
<rect x="135" y="270" width="45" height="45" style="fill: #a57551"/>
<rect x="135" y="270" width="45" height="45" style="fill-opacity:0.2;fill: #ffff00"/>
<rect x="180" y="270" width="45" height="45" style="fill: #ebd1a6"/>
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="360" height="360" viewBox="-180 -270 360 360">
<path
d="M 22,9 C 19.79,9 18,10.79 18,13 C 18,13.89 18.29,14.71 18.78,15.38 C 16.83,16.5 15.5,18.59 15.5,21 C 15.5,23.03 16.44,24.84 17.91,26.03 C 14.91,27.09 10.5,31.58 10.5,39.5 L 33.5,39.5 C 33.5,31.58 29.09,27.09 26.09,26.03 C 27.56,24.84 28.5,23.03 28.5,21 C 28.5,18.59 27.17,16.5 25.22,15.38 C 25.71,14.71 26,13.89 26,13 C 26,10.79 24.21,9 22,9 z "
style="opacity:1; fill:#ffffff; fill-opacity:1; fill-rule:nonzero; stroke:#000000; stroke-width:1.5; stroke-linecap:round; stroke-linejoin:miter; stroke-miterlimit:4; stroke-dasharray:none; stroke-opacity:1;" />
</svg>
<rect x="225" y="270" width="45" height="45" style="fill: #a57551"/>
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="360" height="360" viewBox="-225 -270 360 360">
<path
d="M 22,9 C 19.79,9 18,10.79 18,13 C 18,13.89 18.29,14.71 18.78,15.38 C 16.83,16.5 15.5,18.59 15.5,21 C 15.5,23.03 16.44,24.84 17.91,26.03 C 14.91,27.09 10.5,31.58 10.5,39.5 L 33.5,39.5 C 33.5,31.58 29.09,27.09 26.09,26.03 C 27.56,24.84 28.5,23.03 28.5,21 C 28.5,18.59 27.17,16.5 25.22,15.38 C 25.71,14.71 26,13.89 26,13 C 26,10.79 24.21,9 22,9 z "
style="opacity:1; fill:#ffffff; fill-opacity:1; fill-rule:nonzero; stroke:#000000; stroke-width:1.5; stroke-linecap:round; stroke-linejoin:miter; stroke-miterlimit:4; stroke-dasharray:none; stroke-opacity:1;" />
</svg>
<rect x="270" y="270" width="45" height="45" style="fill: #ebd1a6"/>
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="360" height="360" viewBox="-270 -270 360 360">
<path
d="M 22,9 C 19.79,9 18,10.79 18,13 C 18,13.89 18.29,14.71 18.78,15.38 C 16.83,16.5 15.5,18.59 15.5,21 C 15.5,23.03 16.44,24.84 17.91,26.03 C 14.91,27.09 10.5,31.58 10.5,39.5 L 33.5,39.5 C 33.5,31.58 29.09,27.09 26.09,26.03 C 27.56,24.84 28.5,23.03 28.5,21 C 28.5,18.59 27.17,16.5 25.22,15.38 C 25.71,14.71 26,13.89 26,13 C 26,10.79 24.21,9 22,9 z "
style="opacity:1; fill:#ffffff; fill-opacity:1; fill-rule:nonzero; stroke:#000000; stroke-width:1.5; stroke-linecap:round; stroke-linejoin:miter; stroke-miterlimit:4; stroke-dasharray:none; stroke-opacity:1;" />
</svg>
<rect x="315" y="270" width="45" height="45" style="fill: #a57551"/>
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="360" height="360" viewBox="-315 -270 360 360">
<path
d="M 22,9 C 19.79,9 18,10.79 18,13 C 18,13.89 18.29,14.71 18.78,15.38 C 16.83,16.5 15.5,18.59 15.5,21 C 15.5,23.03 16.44,24.84 17.91,26.03 C 14.91,27.09 10.5,31.58 10.5,39.5 L 33.5,39.5 C 33.5,31.58 29.09,27.09 26.09,26.03 C 27.56,24.84 28.5,23.03 28.5,21 C 28.5,18.59 27.17,16.5 25.22,15.38 C 25.71,14.71 26,13.89 26,13 C 26,10.79 24.21,9 22,9 z "
style="opacity:1; fill:#ffffff; fill-opacity:1; fill-rule:nonzero; stroke:#000000; stroke-width:1.5; stroke-linecap:round; stroke-linejoin:miter; stroke-miterlimit:4; stroke-dasharray:none; stroke-opacity:1;" />
</svg>
<rect x="0" y="225" width="45" height="45" style="fill: #a57551"/>
<text x="2" y="236" style="font-size:11px;fill: #ebd1a6">3</text>
<rect x="45" y="225" width="45" height="45" style="fill: #ebd1a6"/>
<rect x="90" y="225" width="45" height="45" style="fill: #a57551"/>
<rect x="135" y="225" width="45" height="45" style="fill: #ebd1a6"/>
<rect x="180" y="225" width="45" height="45" style="fill: #a57551"/>
<rect x="225" y="225" width="45" height="45" style="fill: #ebd1a6"/>
<rect x="270" y="225" width="45" height="45" style="fill: #a57551"/>
<rect x="315" y="225" width="45" height="45" style="fill: #ebd1a6"/>
<rect x="0" y="180" width="45" height="45" style="fill: #ebd1a6"/>
<text x="2" y="191" style="font-size:11px;fill: #a57551">4</text>
<rect x="45" y="180" width="45" height="45" style="fill: #a57551"/>
<rect x="90" y="180" width="45" height="45" style="fill: #ebd1a6"/>
<rect x="135" y="180" width="45" height="45" style="fill: #a57551"/>
<rect x="135" y="180" width="45" height="45" style="fill-opacity:0.2;fill: #ffff00"/>
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="360" height="360" viewBox="-135 -180 360 360">
<path
d="M 22,9 C 19.79,9 18,10.79 18,13 C 18,13.89 18.29,14.71 18.78,15.38 C 16.83,16.5 15.5,18.59 15.5,21 C 15.5,23.03 16.44,24.84 17.91,26.03 C 14.91,27.09 10.5,31.58 10.5,39.5 L 33.5,39.5 C 33.5,31.58 29.09,27.09 26.09,26.03 C 27.56,24.84 28.5,23.03 28.5,21 C 28.5,18.59 27.17,16.5 25.22,15.38 C 25.71,14.71 26,13.89 26,13 C 26,10.79 24.21,9 22,9 z "
style="opacity:1; fill:#ffffff; fill-opacity:1; fill-rule:nonzero; stroke:#000000; stroke-width:1.5; stroke-linecap:round; stroke-linejoin:miter; stroke-miterlimit:4; stroke-dasharray:none; stroke-opacity:1;" />
</svg>
<rect x="180" y="180" width="45" height="45" style="fill: #ebd1a6"/>
<rect x="225" y="180" width="45" height="45" style="fill: #a57551"/>
<rect x="270" y="180" width="45" height="45" style="fill: #ebd1a6"/>
<rect x="315" y="180" width="45" height="45" style="fill: #a57551"/>
<rect x="0" y="135" width="45" height="45" style="fill: #a57551"/>
<text x="2" y="146" style="font-size:11px;fill: #ebd1a6">5</text>
<rect x="45" y="135" width="45" height="45" style="fill: #ebd1a6"/>
<rect x="90" y="135" width="45" height="45" style="fill: #a57551"/>
<rect x="135" y="135" width="45" height="45" style="fill: #ebd1a6"/>
<rect x="180" y="135" width="45" height="45" style="fill: #a57551"/>
<rect x="225" y="135" width="45" height="45" style="fill: #ebd1a6"/>
<rect x="270" y="135" width="45" height="45" style="fill: #a57551"/>
<rect x="315" y="135" width="45" height="45" style="fill: #ebd1a6"/>
<rect x="0" y="90" width="45" height="45" style="fill: #ebd1a6"/>
<text x="2" y="101" style="font-size:11px;fill: #a57551">6</text>
<rect x="45" y="90" width="45" height="45" style="fill: #a57551"/>
<rect x="90" y="90" width="45" height="45" style="fill: #ebd1a6"/>
<rect x="135" y="90" width="45" height="45" style="fill: #a57551"/>
<rect x="180" y="90" width="45" height="45" style="fill: #ebd1a6"/>
<rect x="225" y="90" width="45" height="45" style="fill: #a57551"/>
<rect x="270" y="90" width="45" height="45" style="fill: #ebd1a6"/>
<rect x="315" y="90" width="45" height="45" style="fill: #a57551"/>
<rect x="0" y="45" width="45" height="45" style="fill: #a57551"/>
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="360" height="360" viewBox="0 -45 360 360">
<path
d="M 22,9 C 19.79,9 18,10.79 18,13 C 18,13.89 18.29,14.71 18.78,15.38 C 16.83,16.5 15.5,18.59 15.5,21 C 15.5,23.03 16.44,24.84 17.91,26.03 C 14.91,27.09 10.5,31.58 10.5,39.5 L 33.5,39.5 C 33.5,31.58 29.09,27.09 26.09,26.03 C 27.56,24.84 28.5,23.03 28.5,21 C 28.5,18.59 27.17,16.5 25.22,15.38 C 25.71,14.71 26,13.89 26,13 C 26,10.79 24.21,9 22,9 z "
style="opacity:1; fill:#000000; fill-opacity:1; fill-rule:nonzero; stroke:#000000; stroke-width:1.5; stroke-linecap:round; stroke-linejoin:miter; stroke-miterlimit:4; stroke-dasharray:none; stroke-opacity:1;" />
</svg>
<text x="2" y="56" style="font-size:11px;fill: #ebd1a6">7</text>
<rect x="45" y="45" width="45" height="45" style="fill: #ebd1a6"/>
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="360" height="360" viewBox="-45 -45 360 360">
<path
d="M 22,9 C 19.79,9 18,10.79 18,13 C 18,13.89 18.29,14.71 18.78,15.38 C 16.83,16.5 15.5,18.59 15.5,21 C 15.5,23.03 16.44,24.84 17.91,26.03 C 14.91,27.09 10.5,31.58 10.5,39.5 L 33.5,39.5 C 33.5,31.58 29.09,27.09 26.09,26.03 C 27.56,24.84 28.5,23.03 28.5,21 C 28.5,18.59 27.17,16.5 25.22,15.38 C 25.71,14.71 26,13.89 26,13 C 26,10.79 24.21,9 22,9 z "
style="opacity:1; fill:#000000; fill-opacity:1; fill-rule:nonzero; stroke:#000000; stroke-width:1.5; stroke-linecap:round; stroke-linejoin:miter; stroke-miterlimit:4; stroke-dasharray:none; stroke-opacity:1;" />
</svg>
<rect x="90" y="45" width="45" height="45" style="fill: #a57551"/>
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="360" height="360" viewBox="-90 -45 360 360">
<path
d="M 22,9 C 19.79,9 18,10.79 18,13 C 18,13.89 18.29,14.71 18.78,15.38 C 16.83,16.5 15.5,18.59 15.5,21 C 15.5,23.03 16.44,24.84 17.91,26.03 C 14.91,27.09 10.5,31.58 10.5,39.5 L 33.5,39.5 C 33.5,31.58 29.09,27.09 26.09,26.03 C 27.56,24.84 28.5,23.03 28.5,21 C 28.5,18.59 27.17,16.5 25.22,15.38 C 25.71,14.71 26,13.89 26,13 C 26,10.79 24.21,9 22,9 z "
style="opacity:1; fill:#000000; fill-opacity:1; fill-rule:nonzero; stroke:#000000; stroke-width:1.5; stroke-linecap:round; stroke-linejoin:miter; stroke-miterlimit:4; stroke-dasharray:none; stroke-opacity:1;" />
</svg>
<rect x="135" y="45" width="45" height="45" style="fill: #ebd1a6"/>
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="360" height="360" viewBox="-135 -45 360 360">
<path
d="M 22,9 C 19.79,9 18,10.79 18,13 C 18,13.89 18.29,14.71 18.78,15.38 C 16.83,16.5 15.5,18.59 15.5,21 C 15.5,23.03 16.44,24.84 17.91,26.03 C 14.91,27.09 10.5,31.58 10.5,39.5 L 33.5,39.5 C 33.5,31.58 29.09,27.09 26.09,26.03 C 27.56,24.84 28.5,23.03 28.5,21 C 28.5,18.59 27.17,16.5 25.22,15.38 C 25.71,14.71 26,13.89 26,13 C 26,10.79 24.21,9 22,9 z "
style="opacity:1; fill:#000000; fill-opacity:1; fill-rule:nonzero; stroke:#000000; stroke-width:1.5; stroke-linecap:round; stroke-linejoin:miter; stroke-miterlimit:4; stroke-dasharray:none; stroke-opacity:1;" />
</svg>
<rect x="180" y="45" width="45" height="45" style="fill: #a57551"/>
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="360" height="360" viewBox="-180 -45 360 360">
<path
d="M 22,9 C 19.79,9 18,10.79 18,13 C 18,13.89 18.29,14.71 18.78,15.38 C 16.83,16.5 15.5,18.59 15.5,21 C 15.5,23.03 16.44,24.84 17.91,26.03 C 14.91,27.09 10.5,31.58 10.5,39.5 L 33.5,39.5 C 33.5,31.58 29.09,27.09 26.09,26.03 C 27.56,24.84 28.5,23.03 28.5,21 C 28.5,18.59 27.17,16.5 25.22,15.38 C 25.71,14.71 26,13.89 26,13 C 26,10.79 24.21,9 22,9 z "
style="opacity:1; fill:#000000; fill-opacity:1; fill-rule:nonzero; stroke:#000000; stroke-width:1.5; stroke-linecap:round; stroke-linejoin:miter; stroke-miterlimit:4; stroke-dasharray:none; stroke-opacity:1;" />
</svg>
<rect x="225" y="45" width="45" height="45" style="fill: #ebd1a6"/>
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="360" height="360" viewBox="-225 -45 360 360">
<path
d="M 22,9 C 19.79,9 18,10.79 18,13 C 18,13.89 18.29,14.71 18.78,15.38 C 16.83,16.5 15.5,18.59 15.5,21 C 15.5,23.03 16.44,24.84 17.91,26.03 C 14.91,27.09 10.5,31.58 10.5,39.5 L 33.5,39.5 C 33.5,31.58 29.09,27.09 26.09,26.03 C 27.56,24.84 28.5,23.03 28.5,21 C 28.5,18.59 27.17,16.5 25.22,15.38 C 25.71,14.71 26,13.89 26,13 C 26,10.79 24.21,9 22,9 z "
style="opacity:1; fill:#000000; fill-opacity:1; fill-rule:nonzero; stroke:#000000; stroke-width:1.5; stroke-linecap:round; stroke-linejoin:miter; stroke-miterlimit:4; stroke-dasharray:none; stroke-opacity:1;" />
</svg>
<rect x="270" y="45" width="45" height="45" style="fill: #a57551"/>
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="360" height="360" viewBox="-270 -45 360 360">
<path
d="M 22,9 C 19.79,9 18,10.79 18,13 C 18,13.89 18.29,14.71 18.78,15.38 C 16.83,16.5 15.5,18.59 15.5,21 C 15.5,23.03 16.44,24.84 17.91,26.03 C 14.91,27.09 10.5,31.58 10.5,39.5 L 33.5,39.5 C 33.5,31.58 29.09,27.09 26.09,26.03 C 27.56,24.84 28.5,23.03 28.5,21 C 28.5,18.59 27.17,16.5 25.22,15.38 C 25.71,14.71 26,13.89 26,13 C 26,10.79 24.21,9 22,9 z "
style="opacity:1; fill:#000000; fill-opacity:1; fill-rule:nonzero; stroke:#000000; stroke-width:1.5; stroke-linecap:round; stroke-linejoin:miter; stroke-miterlimit:4; stroke-dasharray:none; stroke-opacity:1;" />
</svg>
<rect x="315" y="45" width="45" height="45" style="fill: #ebd1a6"/>
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="360" height="360" viewBox="-315 -45 360 360">
<path
d="M 22,9 C 19.79,9 18,10.79 18,13 C 18,13.89 18.29,14.71 18.78,15.38 C 16.83,16.5 15.5,18.59 15.5,21 C 15.5,23.03 16.44,24.84 17.91,26.03 C 14.91,27.09 10.5,31.58 10.5,39.5 L 33.5,39.5 C 33.5,31.58 29.09,27.09 26.09,26.03 C 27.56,24.84 28.5,23.03 28.5,21 C 28.5,18.59 27.17,16.5 25.22,15.38 C 25.71,14.71 26,13.89 26,13 C 26,10.79 24.21,9 22,9 z "
style="opacity:1; fill:#000000; fill-opacity:1; fill-rule:nonzero; stroke:#000000; stroke-width:1.5; stroke-linecap:round; stroke-linejoin:miter; stroke-miterlimit:4; stroke-dasharray:none; stroke-opacity:1;" />
</svg>
<rect x="0" y="0" width="45" height="45" style="fill: #ebd1a6"/>
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="360" height="360" viewBox="0 0 360 360">
<g style="opacity:1; fill:000000; fill-opacity:1; fill-rule:evenodd; stroke:#000000; stroke-width:1.5; stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4; stroke-dasharray:none; stroke-opacity:1;">
<path
d="M 9,39 L 36,39 L 36,36 L 9,36 L 9,39 z "
style="stroke-linecap:butt;" />
<path
d="M 12.5,32 L 14,29.5 L 31,29.5 L 32.5,32 L 12.5,32 z "
style="stroke-linecap:butt;" />
<path
d="M 12,36 L 12,32 L 33,32 L 33,36 L 12,36 z "
style="stroke-linecap:butt;" />
<path
d="M 14,29.5 L 14,16.5 L 31,16.5 L 31,29.5 L 14,29.5 z "
style="stroke-linecap:butt;stroke-linejoin:miter;" />
<path
d="M 14,16.5 L 11,14 L 34,14 L 31,16.5 L 14,16.5 z "
style="stroke-linecap:butt;" />
<path
d="M 11,14 L 11,9 L 15,9 L 15,11 L 20,11 L 20,9 L 25,9 L 25,11 L 30,11 L 30,9 L 34,9 L 34,14 L 11,14 z "
style="stroke-linecap:butt;" />
<path
d="M 12,35.5 L 33,35.5 L 33,35.5"
style="fill:none; stroke:#ffffff; stroke-width:1; stroke-linejoin:miter;" />
<path
d="M 13,31.5 L 32,31.5"
style="fill:none; stroke:#ffffff; stroke-width:1; stroke-linejoin:miter;" />
<path
d="M 14,29.5 L 31,29.5"
style="fill:none; stroke:#ffffff; stroke-width:1; stroke-linejoin:miter;" />
<path
d="M 14,16.5 L 31,16.5"
style="fill:none; stroke:#ffffff; stroke-width:1; stroke-linejoin:miter;" />
<path
d="M 11,14 L 34,14"
style="fill:none; stroke:#ffffff; stroke-width:1; stroke-linejoin:miter;" />
</g>
</svg>
<text x="2" y="11" style="font-size:11px;fill: #a57551">8</text>
<rect x="45" y="0" width="45" height="45" style="fill: #a57551"/>
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="360" height="360" viewBox="-45 0 360 360">
<g style="opacity:1; fill:none; fill-opacity:1; fill-rule:evenodd; stroke:#000000; stroke-width:1.5; stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4; stroke-dasharray:none; stroke-opacity:1;">
<path
d="M 22,10 C 32.5,11 38.5,18 38,39 L 15,39 C 15,30 25,32.5 23,18"
style="fill:#000000; stroke:#000000;" />
<path
d="M 24,18 C 24.38,20.91 18.45,25.37 16,27 C 13,29 13.18,31.34 11,31 C 9.958,30.06 12.41,27.96 11,28 C 10,28 11.19,29.23 10,30 C 9,30 5.997,31 6,26 C 6,24 12,14 12,14 C 12,14 13.89,12.1 14,10.5 C 13.27,9.506 13.5,8.5 13.5,7.5 C 14.5,6.5 16.5,10 16.5,10 L 18.5,10 C 18.5,10 19.28,8.008 21,7 C 22,7 22,10 22,10"
style="fill:#000000; stroke:#000000;" />
<path
d="M 9.5 25.5 A 0.5 0.5 0 1 1 8.5,25.5 A 0.5 0.5 0 1 1 9.5 25.5 z"
style="fill:#ffffff; stroke:#ffffff;" />
<path
d="M 15 15.5 A 0.5 1.5 0 1 1 14,15.5 A 0.5 1.5 0 1 1 15 15.5 z"
transform="matrix(0.866,0.5,-0.5,0.866,9.693,-5.173)"
style="fill:#ffffff; stroke:#ffffff;" />
<path
d="M 24.55,10.4 L 24.1,11.85 L 24.6,12 C 27.75,13 30.25,14.49 32.5,18.75 C 34.75,23.01 35.75,29.06 35.25,39 L 35.2,39.5 L 37.45,39.5 L 37.5,39 C 38,28.94 36.62,22.15 34.25,17.66 C 31.88,13.17 28.46,11.02 25.06,10.5 L 24.55,10.4 z "
style="fill:#ffffff; stroke:none;" />
</g>
</svg>
<rect x="90" y="0" width="45" height="45" style="fill: #ebd1a6"/>
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="360" height="360" viewBox="-90 0 360 360">
<g style="opacity:1; fill:none; fill-rule:evenodd; fill-opacity:1; stroke:#000000; stroke-width:1.5; stroke-linecap:round; stroke-linejoin:round; stroke-miterlimit:4; stroke-dasharray:none; stroke-opacity:1;">
<g style="fill:#000000; stroke:#000000; stroke-linecap:butt;">
<path
d="M 9,36 C 12.39,35.03 19.11,36.43 22.5,34 C 25.89,36.43 32.61,35.03 36,36 C 36,36 37.65,36.54 39,38 C 38.32,38.97 37.35,38.99 36,38.5 C 32.61,37.53 25.89,38.96 22.5,37.5 C 19.11,38.96 12.39,37.53 9,38.5 C 7.646,38.99 6.677,38.97 6,38 C 7.354,36.06 9,36 9,36 z" />
<path
d="M 15,32 C 17.5,34.5 27.5,34.5 30,32 C 30.5,30.5 30,30 30,30 C 30,27.5 27.5,26 27.5,26 C 33,24.5 33.5,14.5 22.5,10.5 C 11.5,14.5 12,24.5 17.5,26 C 17.5,26 15,27.5 15,30 C 15,30 14.5,30.5 15,32 z" />
<path
d="M 25 8 A 2.5 2.5 0 1 1 20,8 A 2.5 2.5 0 1 1 25 8 z" />
</g>
<path
d="M 17.5,26 L 27.5,26 M 15,30 L 30,30 M 22.5,15.5 L 22.5,20.5 M 20,18 L 25,18"
style="fill:none; stroke:#ffffff; stroke-linejoin:miter;" />
</g>
</svg>
<rect x="135" y="0" width="45" height="45" style="fill: #a57551"/>
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="360" height="360" viewBox="-135 0 360 360">
<g style="opacity:1; fill:000000; fill-opacity:1; fill-rule:evenodd; stroke:#000000; stroke-width:1.5; stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4; stroke-dasharray:none; stroke-opacity:1;">
<g style="fill:#000000; stroke:none;">
<circle cx="6" cy="12" r="2.75" />
<circle cx="14" cy="9" r="2.75" />
<circle cx="22.5" cy="8" r="2.75" />
<circle cx="31" cy="9" r="2.75" />
<circle cx="39" cy="12" r="2.75" />
</g>
<path
d="M 9,26 C 17.5,24.5 30,24.5 36,26 L 38.5,13.5 L 31,25 L 30.7,10.9 L 25.5,24.5 L 22.5,10 L 19.5,24.5 L 14.3,10.9 L 14,25 L 6.5,13.5 L 9,26 z"
style="stroke-linecap:butt; stroke:#000000;" />
<path
d="M 9,26 C 9,28 10.5,28 11.5,30 C 12.5,31.5 12.5,31 12,33.5 C 10.5,34.5 10.5,36 10.5,36 C 9,37.5 11,38.5 11,38.5 C 17.5,39.5 27.5,39.5 34,38.5 C 34,38.5 35.5,37.5 34,36 C 34,36 34.5,34.5 33,33.5 C 32.5,31 32.5,31.5 33.5,30 C 34.5,28 36,28 36,26 C 27.5,24.5 17.5,24.5 9,26 z"
style="stroke-linecap:butt;" />
<path
d="M 11,38.5 A 35,35 1 0 0 34,38.5"
style="fill:none; stroke:#000000; stroke-linecap:butt;" />
<path
d="M 11,29 A 35,35 1 0 1 34,29"
style="fill:none; stroke:#ffffff;" />
<path
d="M 12.5,31.5 L 32.5,31.5"
style="fill:none; stroke:#ffffff;" />
<path
d="M 11.5,34.5 A 35,35 1 0 0 33.5,34.5"
style="fill:none; stroke:#ffffff;" />
<path
d="M 10.5,37.5 A 35,35 1 0 0 34.5,37.5"
style="fill:none; stroke:#ffffff;" />
</g>
</svg>
<rect x="180" y="0" width="45" height="45" style="fill: #ebd1a6"/>
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="360" height="360" viewBox="-180 0 360 360">
<g style="fill:none; fill-opacity:1; fill-rule:evenodd; stroke:#000000; stroke-width:1.5; stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4; stroke-dasharray:none; stroke-opacity:1;">
<path
d="M 22.5,11.63 L 22.5,6"
style="fill:none; stroke:#000000; stroke-linejoin:miter;"
id="path6570" />
<path
d="M 22.5,25 C 22.5,25 27,17.5 25.5,14.5 C 25.5,14.5 24.5,12 22.5,12 C 20.5,12 19.5,14.5 19.5,14.5 C 18,17.5 22.5,25 22.5,25"
style="fill:#000000;fill-opacity:1; stroke-linecap:butt; stroke-linejoin:miter;" />
<path
d="M 11.5,37 C 17,40.5 27,40.5 32.5,37 L 32.5,30 C 32.5,30 41.5,25.5 38.5,19.5 C 34.5,13 25,16 22.5,23.5 L 22.5,27 L 22.5,23.5 C 19,16 9.5,13 6.5,19.5 C 3.5,25.5 11.5,29.5 11.5,29.5 L 11.5,37 z "
style="fill:#000000; stroke:#000000;" />
<path
d="M 20,8 L 25,8"
style="fill:none; stroke:#000000; stroke-linejoin:miter;" />
<path
d="M 32,29.5 C 32,29.5 40.5,25.5 38.03,19.85 C 34.15,14 25,18 22.5,24.5 L 22.51,26.6 L 22.5,24.5 C 20,18 9.906,14 6.997,19.85 C 4.5,25.5 11.85,28.85 11.85,28.85"
style="fill:none; stroke:#ffffff;" />
<path
d="M 11.5,30 C 17,27 27,27 32.5,30 M 11.5,33.5 C 17,30.5 27,30.5 32.5,33.5 M 11.5,37 C 17,34 27,34 32.5,37"
style="fill:none; stroke:#ffffff;" />
</g>
</svg>
<rect x="225" y="0" width="45" height="45" style="fill: #a57551"/>
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="360" height="360" viewBox="-225 0 360 360">
<g style="opacity:1; fill:none; fill-rule:evenodd; fill-opacity:1; stroke:#000000; stroke-width:1.5; stroke-linecap:round; stroke-linejoin:round; stroke-miterlimit:4; stroke-dasharray:none; stroke-opacity:1;">
<g style="fill:#000000; stroke:#000000; stroke-linecap:butt;">
<path
d="M 9,36 C 12.39,35.03 19.11,36.43 22.5,34 C 25.89,36.43 32.61,35.03 36,36 C 36,36 37.65,36.54 39,38 C 38.32,38.97 37.35,38.99 36,38.5 C 32.61,37.53 25.89,38.96 22.5,37.5 C 19.11,38.96 12.39,37.53 9,38.5 C 7.646,38.99 6.677,38.97 6,38 C 7.354,36.06 9,36 9,36 z" />
<path
d="M 15,32 C 17.5,34.5 27.5,34.5 30,32 C 30.5,30.5 30,30 30,30 C 30,27.5 27.5,26 27.5,26 C 33,24.5 33.5,14.5 22.5,10.5 C 11.5,14.5 12,24.5 17.5,26 C 17.5,26 15,27.5 15,30 C 15,30 14.5,30.5 15,32 z" />
<path
d="M 25 8 A 2.5 2.5 0 1 1 20,8 A 2.5 2.5 0 1 1 25 8 z" />
</g>
<path
d="M 17.5,26 L 27.5,26 M 15,30 L 30,30 M 22.5,15.5 L 22.5,20.5 M 20,18 L 25,18"
style="fill:none; stroke:#ffffff; stroke-linejoin:miter;" />
</g>
</svg>
<rect x="270" y="0" width="45" height="45" style="fill: #ebd1a6"/>
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="360" height="360" viewBox="-270 0 360 360">
<g style="opacity:1; fill:none; fill-opacity:1; fill-rule:evenodd; stroke:#000000; stroke-width:1.5; stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4; stroke-dasharray:none; stroke-opacity:1;">
<path
d="M 22,10 C 32.5,11 38.5,18 38,39 L 15,39 C 15,30 25,32.5 23,18"
style="fill:#000000; stroke:#000000;" />
<path
d="M 24,18 C 24.38,20.91 18.45,25.37 16,27 C 13,29 13.18,31.34 11,31 C 9.958,30.06 12.41,27.96 11,28 C 10,28 11.19,29.23 10,30 C 9,30 5.997,31 6,26 C 6,24 12,14 12,14 C 12,14 13.89,12.1 14,10.5 C 13.27,9.506 13.5,8.5 13.5,7.5 C 14.5,6.5 16.5,10 16.5,10 L 18.5,10 C 18.5,10 19.28,8.008 21,7 C 22,7 22,10 22,10"
style="fill:#000000; stroke:#000000;" />
<path
d="M 9.5 25.5 A 0.5 0.5 0 1 1 8.5,25.5 A 0.5 0.5 0 1 1 9.5 25.5 z"
style="fill:#ffffff; stroke:#ffffff;" />
<path
d="M 15 15.5 A 0.5 1.5 0 1 1 14,15.5 A 0.5 1.5 0 1 1 15 15.5 z"
transform="matrix(0.866,0.5,-0.5,0.866,9.693,-5.173)"
style="fill:#ffffff; stroke:#ffffff;" />
<path
d="M 24.55,10.4 L 24.1,11.85 L 24.6,12 C 27.75,13 30.25,14.49 32.5,18.75 C 34.75,23.01 35.75,29.06 35.25,39 L 35.2,39.5 L 37.45,39.5 L 37.5,39 C 38,28.94 36.62,22.15 34.25,17.66 C 31.88,13.17 28.46,11.02 25.06,10.5 L 24.55,10.4 z "
style="fill:#ffffff; stroke:none;" />
</g>
</svg>
<rect x="315" y="0" width="45" height="45" style="fill: #a57551"/>
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="360" height="360" viewBox="-315 0 360 360">
<g style="opacity:1; fill:000000; fill-opacity:1; fill-rule:evenodd; stroke:#000000; stroke-width:1.5; stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4; stroke-dasharray:none; stroke-opacity:1;">
<path
d="M 9,39 L 36,39 L 36,36 L 9,36 L 9,39 z "
style="stroke-linecap:butt;" />
<path
d="M 12.5,32 L 14,29.5 L 31,29.5 L 32.5,32 L 12.5,32 z "
style="stroke-linecap:butt;" />
<path
d="M 12,36 L 12,32 L 33,32 L 33,36 L 12,36 z "
style="stroke-linecap:butt;" />
<path
d="M 14,29.5 L 14,16.5 L 31,16.5 L 31,29.5 L 14,29.5 z "
style="stroke-linecap:butt;stroke-linejoin:miter;" />
<path
d="M 14,16.5 L 11,14 L 34,14 L 31,16.5 L 14,16.5 z "
style="stroke-linecap:butt;" />
<path
d="M 11,14 L 11,9 L 15,9 L 15,11 L 20,11 L 20,9 L 25,9 L 25,11 L 30,11 L 30,9 L 34,9 L 34,14 L 11,14 z "
style="stroke-linecap:butt;" />
<path
d="M 12,35.5 L 33,35.5 L 33,35.5"
style="fill:none; stroke:#ffffff; stroke-width:1; stroke-linejoin:miter;" />
<path
d="M 13,31.5 L 32,31.5"
style="fill:none; stroke:#ffffff; stroke-width:1; stroke-linejoin:miter;" />
<path
d="M 14,29.5 L 31,29.5"
style="fill:none; stroke:#ffffff; stroke-width:1; stroke-linejoin:miter;" />
<path
d="M 14,16.5 L 31,16.5"
style="fill:none; stroke:#ffffff; stroke-width:1; stroke-linejoin:miter;" />
<path
d="M 11,14 L 34,14"
style="fill:none; stroke:#ffffff; stroke-width:1; stroke-linejoin:miter;" />
</g>
</svg>
</svg>

After

Width:  |  Height:  |  Size: 37 KiB

72
move.go

@ -0,0 +1,72 @@ @@ -0,0 +1,72 @@
package chess
// A MoveTag represents a notable consequence of a move.
type MoveTag uint16
const (
// KingSideCastle indicates that the move is a king side castle.
KingSideCastle MoveTag = 1 << iota
// QueenSideCastle indicates that the move is a queen side castle.
QueenSideCastle
// Capture indicates that the move captures a piece.
Capture
// EnPassant indicates that the move captures via en passant.
EnPassant
// Check indicates that the move puts the opposing player in check.
Check
// inCheck indicates that the move puts the moving player in check and
// is therefore invalid.
inCheck
)
// A Move is the movement of a piece from one square to another.
type Move struct {
s1 Square
s2 Square
promo PieceType
tags MoveTag
}
// String returns a string useful for debugging. String doesn't return
// algebraic notation.
func (m *Move) String() string {
return m.s1.String() + m.s2.String() + m.promo.String()
}
// S1 returns the origin square of the move.
func (m *Move) S1() Square {
return m.s1
}
// S2 returns the destination square of the move.
func (m *Move) S2() Square {
return m.s2
}
// Promo returns promotion piece type of the move.
func (m *Move) Promo() PieceType {
return m.promo
}
// HasTag returns true if the move contains the MoveTag given.
func (m *Move) HasTag(tag MoveTag) bool {
return (tag & m.tags) > 0
}
func (m *Move) addTag(tag MoveTag) {
m.tags = m.tags | tag
}
type moveSlice []*Move
func (a moveSlice) find(m *Move) *Move {
if m == nil {
return nil
}
for _, move := range a {
if move.String() == m.String() {
return move
}
}
return nil
}

302
move_test.go

@ -0,0 +1,302 @@ @@ -0,0 +1,302 @@
package chess
import (
"log"
"testing"
)
type moveTest struct {
pos *Position
m *Move
postPos *Position
}
var (
validMoves = []moveTest{
// pawn moves
{m: &Move{s1: E2, s2: E4}, pos: unsafeFEN("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1")},
{m: &Move{s1: A2, s2: A3}, pos: unsafeFEN("8/8/8/8/8/8/P7/8 w - - 0 1")},
{m: &Move{s1: A7, s2: A6}, pos: unsafeFEN("8/p7/8/8/8/8/8/8 b - - 0 1")},
{m: &Move{s1: A7, s2: A5}, pos: unsafeFEN("8/p7/8/8/8/8/8/8 b - - 0 1")},
{m: &Move{s1: C4, s2: B5}, pos: unsafeFEN("8/8/8/1p1p4/2P5/8/8/8 w - - 0 1")},
{m: &Move{s1: C4, s2: D5}, pos: unsafeFEN("8/8/8/1p1p4/2P5/8/8/8 w - - 0 1")},
{m: &Move{s1: C4, s2: C5}, pos: unsafeFEN("8/8/8/1p1p4/2P5/8/8/8 w - - 0 1")},
{m: &Move{s1: C5, s2: B4}, pos: unsafeFEN("8/8/8/2p5/1P1P4/8/8/8 b - - 0 1")},
{m: &Move{s1: C5, s2: D4}, pos: unsafeFEN("8/8/8/2p5/1P1P4/8/8/8 b - - 0 1")},
{m: &Move{s1: C5, s2: C4}, pos: unsafeFEN("8/8/8/2p5/1P1P4/8/8/8 b - - 0 1")},
{m: &Move{s1: A4, s2: B3}, pos: unsafeFEN("2r3k1/1q1nbppp/r3p3/3pP3/pPpP4/P1Q2N2/2RN1PPP/2R4K b - b3 0 23")},
{m: &Move{s1: A2, s2: A1, promo: Queen}, pos: unsafeFEN("8/8/8/8/8/8/p7/8 b - - 0 1")},
{m: &Move{s1: E7, s2: E6}, pos: unsafeFEN("r2qkbnr/pppnpppp/8/3p4/6b1/1P3NP1/PBPPPP1P/RN1QKB1R b KQkq - 2 4")},
// knight moves
{m: &Move{s1: E4, s2: F6}, pos: unsafeFEN("8/8/8/3pp3/4N3/8/5B2/8 w - - 0 1")},
{m: &Move{s1: E4, s2: D6}, pos: unsafeFEN("8/8/8/3pp3/4N3/8/5B2/8 w - - 0 1")},
{m: &Move{s1: E4, s2: G5}, pos: unsafeFEN("8/8/8/3pp3/4N3/8/5B2/8 w - - 0 1")},
{m: &Move{s1: E4, s2: G3}, pos: unsafeFEN("8/8/8/3pp3/4N3/8/5B2/8 w - - 0 1")},
{m: &Move{s1: E4, s2: D2}, pos: unsafeFEN("8/8/8/3pp3/4N3/8/5B2/8 w - - 0 1")},
{m: &Move{s1: E4, s2: C3}, pos: unsafeFEN("8/8/8/3pp3/4N3/8/5B2/8 w - - 0 1")},
{m: &Move{s1: E4, s2: C5}, pos: unsafeFEN("8/8/8/3pp3/4N3/8/5B2/8 w - - 0 1")},
{m: &Move{s1: B8, s2: D7}, pos: unsafeFEN("rn1qkb1r/pp3ppp/2p1pn2/3p4/2PP4/2NQPN2/PP3PPP/R1B1K2R b KQkq - 0 7")},
{m: &Move{s1: F6, s2: E4}, pos: unsafeFEN("r1b1k2r/ppp2ppp/2p2n2/4N3/4P3/2P5/PPP2PPP/R1BK3R b kq - 0 8")},
// bishop moves
{m: &Move{s1: E4, s2: H7}, pos: unsafeFEN("8/8/8/3pp3/4B3/5N2/8/8 w - - 0 1")},
{m: &Move{s1: E4, s2: D5}, pos: unsafeFEN("8/8/8/3pp3/4B3/5N2/8/8 w - - 0 1")},
{m: &Move{s1: E4, s2: B1}, pos: unsafeFEN("8/8/8/3pp3/4B3/5N2/8/8 w - - 0 1")},
// rook moves
{m: &Move{s1: B2, s2: B4}, pos: unsafeFEN("8/1p5b/4N3/4p3/8/8/1R6/1B6 w - - 0 1")},
{m: &Move{s1: B2, s2: B7}, pos: unsafeFEN("8/1p5b/4N3/4p3/8/8/1R6/1B6 w - - 0 1")},
{m: &Move{s1: B2, s2: A2}, pos: unsafeFEN("8/1p5b/4N3/4p3/8/8/1R6/1B6 w - - 0 1")},
{m: &Move{s1: B2, s2: H2}, pos: unsafeFEN("8/1p5b/4N3/4p3/8/8/1R6/1B6 w - - 0 1")},
{m: &Move{s1: E1, s2: E8}, pos: unsafeFEN("r3r1k1/p4p1p/3p4/1p4p1/2pP4/2P2P2/PP3P1P/R3RK2 w - g6 0 22")},
// queen moves
{m: &Move{s1: B2, s2: E5}, pos: unsafeFEN("8/1p5b/4N3/4p3/8/8/1Q6/1B6 w - - 0 1")},
{m: &Move{s1: B2, s2: A1}, pos: unsafeFEN("8/1p5b/4N3/4p3/8/8/1Q6/1B6 w - - 0 1")},
{m: &Move{s1: B2, s2: A2}, pos: unsafeFEN("8/1p5b/4N3/4p3/8/8/1Q6/1B6 w - - 0 1")},
{m: &Move{s1: B2, s2: H2}, pos: unsafeFEN("8/1p5b/4N3/4p3/8/8/1Q6/1B6 w - - 0 1")},
{m: &Move{s1: D8, s2: D1}, pos: unsafeFEN("r1bqk2r/ppp2ppp/2p2n2/4N3/4P3/2P5/PPP2PPP/R1BQK2R b KQkq - 0 7")},
// king moves
{m: &Move{s1: E4, s2: E5}, pos: unsafeFEN("5r2/8/8/8/4K3/8/8/8 w - - 0 1")},
{m: &Move{s1: E4, s2: E3}, pos: unsafeFEN("5r2/8/8/8/4K3/8/8/8 w - - 0 1")},
{m: &Move{s1: E4, s2: D3}, pos: unsafeFEN("5r2/8/8/8/4K3/8/8/8 w - - 0 1")},
{m: &Move{s1: E4, s2: D4}, pos: unsafeFEN("5r2/8/8/8/4K3/8/8/8 w - - 0 1")},
{m: &Move{s1: E4, s2: D5}, pos: unsafeFEN("5r2/8/8/8/4K3/8/8/8 w - - 0 1")},
{m: &Move{s1: E4, s2: E5}, pos: unsafeFEN("5r2/8/8/8/4K3/8/8/8 w - - 0 1")},
// castleing
{m: &Move{s1: E1, s2: G1}, pos: unsafeFEN("r3k2r/8/8/8/8/8/8/R3K2R w KQkq - 0 1")},
{m: &Move{s1: E1, s2: C1}, pos: unsafeFEN("r3k2r/8/8/8/8/8/8/R3K2R w KQkq - 0 1")},
{m: &Move{s1: E8, s2: G8}, pos: unsafeFEN("r3k2r/8/8/8/8/8/8/R3K2R b KQkq - 0 1")},
{m: &Move{s1: E8, s2: C8}, pos: unsafeFEN("r3k2r/8/8/8/8/8/8/R3K2R b KQkq - 0 1")},
// king moving in front of enemy pawn http://en.lichess.org/4HXJOtpN#75
{m: &Move{s1: F8, s2: G7}, pos: unsafeFEN("3rrk2/8/2p3P1/1p2nP1p/pP2p3/P1B1NbPB/2P2K2/5R2 b - - 1 38")},
}
invalidMoves = []moveTest{
// out of turn moves
{m: &Move{s1: E7, s2: E5}, pos: unsafeFEN("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1")},
{m: &Move{s1: E2, s2: E4}, pos: unsafeFEN("rnbqkbnr/1ppppppp/p7/8/8/8/PPPPPPPP/RNBQKBNR b KQkq - 0 1")},
// pawn moves
{m: &Move{s1: E2, s2: D3}, pos: unsafeFEN("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1")},
{m: &Move{s1: E2, s2: F3}, pos: unsafeFEN("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1")},
{m: &Move{s1: E2, s2: E5}, pos: unsafeFEN("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1")},
{m: &Move{s1: A2, s2: A1}, pos: unsafeFEN("8/8/8/8/8/8/p7/8 b - - 0 1")},
{m: &Move{s1: E6, s2: E5}, pos: unsafeFEN(`2b1r3/2k2p1B/p2np3/4B3/8/5N2/PP1K1PPP/3R4 b - - 2 1`)},
{m: &Move{s1: H7, s2: H5}, pos: unsafeFEN(`2bqkbnr/rpppp2p/2n2p2/p5pB/5P2/4P3/PPPP2PP/RNBQK1NR b KQk - 4 6`)},
// knight moves
{m: &Move{s1: E4, s2: F2}, pos: unsafeFEN("8/8/8/3pp3/4N3/8/5B2/8 w - - 0 1")},
{m: &Move{s1: E4, s2: F3}, pos: unsafeFEN("8/8/8/3pp3/4N3/8/5B2/8 w - - 0 1")},
// bishop moves
{m: &Move{s1: E4, s2: C6}, pos: unsafeFEN("8/8/8/3pp3/4B3/5N2/8/8 w - - 0 1")},
{m: &Move{s1: E4, s2: E5}, pos: unsafeFEN("8/8/8/3pp3/4B3/5N2/8/8 w - - 0 1")},
{m: &Move{s1: E4, s2: E4}, pos: unsafeFEN("8/8/8/3pp3/4B3/5N2/8/8 w - - 0 1")},
{m: &Move{s1: E4, s2: F3}, pos: unsafeFEN("8/8/8/3pp3/4B3/5N2/8/8 w - - 0 1")},
// rook moves
{m: &Move{s1: B2, s2: B1}, pos: unsafeFEN("8/1p5b/4N3/4p3/8/8/1R6/1B6 w - - 0 1")},
{m: &Move{s1: B2, s2: C3}, pos: unsafeFEN("8/1p5b/4N3/4p3/8/8/1R6/1B6 w - - 0 1")},
{m: &Move{s1: B2, s2: B8}, pos: unsafeFEN("8/1p5b/4N3/4p3/8/8/1R6/1B6 w - - 0 1")},
{m: &Move{s1: B2, s2: G7}, pos: unsafeFEN("8/1p5b/4N3/4p3/8/8/1R6/1B6 w - - 0 1")},
// queen moves
{m: &Move{s1: B2, s2: B1}, pos: unsafeFEN("8/1p5b/4N3/4p3/8/8/1Q6/1B6 w - - 0 1")},
{m: &Move{s1: B2, s2: C4}, pos: unsafeFEN("8/1p5b/4N3/4p3/8/8/1Q6/1B6 w - - 0 1")},
{m: &Move{s1: B2, s2: B8}, pos: unsafeFEN("8/1p5b/4N3/4p3/8/8/1Q6/1B6 w - - 0 1")},
{m: &Move{s1: B2, s2: G7}, pos: unsafeFEN("8/1p5b/4N3/4p3/8/8/1Q6/1B6 w - - 0 1")},
// king moves
{m: &Move{s1: E4, s2: F3}, pos: unsafeFEN("5r2/8/8/8/4K3/8/8/8 w - - 0 1")},
{m: &Move{s1: E4, s2: F4}, pos: unsafeFEN("5r2/8/8/8/4K3/8/8/8 w - - 0 1")},
{m: &Move{s1: E4, s2: F5}, pos: unsafeFEN("5r2/8/8/8/4K3/8/8/8 w - - 0 1")},
// castleing
{m: &Move{s1: E1, s2: B1}, pos: unsafeFEN("r3k2r/8/8/8/8/8/8/R3K2R w KQkq - 0 1")},
{m: &Move{s1: E8, s2: B8}, pos: unsafeFEN("r3k2r/8/8/8/8/8/8/R3K2R b KQkq - 0 1")},
{m: &Move{s1: E1, s2: C1}, pos: unsafeFEN("r3k2r/8/8/8/8/8/8/R2QK2R w KQkq - 0 1")},
{m: &Move{s1: E1, s2: C1}, pos: unsafeFEN("2r1k2r/8/8/8/8/8/8/R3K2R w KQkq - 0 1")},
{m: &Move{s1: E1, s2: C1}, pos: unsafeFEN("3rk2r/8/8/8/8/8/8/R3K2R w KQkq - 0 1")},
{m: &Move{s1: E1, s2: G1}, pos: unsafeFEN("r3k2r/8/8/8/8/8/8/R3K2R w Qkq - 0 1")},
{m: &Move{s1: E1, s2: C1}, pos: unsafeFEN("r3k2r/8/8/8/8/8/8/R3K2R w Kkq - 0 1")},
// invalid promotion for non-pawn move
{m: &Move{s1: B8, s2: D7, promo: Pawn}, pos: unsafeFEN("rn1qkb1r/pp3ppp/2p1pn2/3p4/2PP4/2NQPN2/PP3PPP/R1B1K2R b KQkq - 0 7")},
// en passant on doubled pawn file http://en.lichess.org/TnRtrHxf#24
{m: &Move{s1: E3, s2: F6}, pos: unsafeFEN("r1b2rk1/pp2b1pp/1qn1p3/3pPp2/1P1P4/P2BPN2/6PP/RN1Q1RK1 w - f6 0 13")},
// can't move piece out of pin (even if checking enemy king) http://en.lichess.org/JCRBhXH7#62
{m: &Move{s1: E1, s2: E7}, pos: unsafeFEN("4R3/1r1k2pp/p1p5/1pP5/8/8/1PP3PP/2K1Rr2 w - - 5 32")},
// invalid one up pawn capture
{m: &Move{s1: E6, s2: E5}, pos: unsafeFEN(`2b1r3/2k2p1B/p2np3/4B3/8/5N2/PP1K1PPP/3R4 b - - 2 1`)},
// invalid two up pawn capture
{m: &Move{s1: H7, s2: H5}, pos: unsafeFEN(`2bqkbnr/rpppp2p/2n2p2/p5pB/5P2/4P3/PPPP2PP/RNBQK1NR b KQk - 4 6`)},
// invalid pawn move d5e4
{m: &Move{s1: D5, s2: E4}, pos: unsafeFEN(`rnbqkbnr/pp2pppp/8/2pp4/3P4/4PN2/PPP2PPP/RNBQKB1R b KQkq - 0 3`)},
}
positionUpdates = []moveTest{
{
m: &Move{s1: E2, s2: E4},
pos: unsafeFEN("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"),
postPos: unsafeFEN("rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR b KQkq e3 0 1"),
},
{
m: &Move{s1: E1, s2: G1, tags: KingSideCastle},
pos: unsafeFEN("r3k2r/8/8/8/8/8/8/R3K2R w KQkq - 0 1"),
postPos: unsafeFEN("r3k2r/8/8/8/8/8/8/R4RK1 b kq - 0 1"),
},
{
m: &Move{s1: A4, s2: B3, tags: EnPassant},
pos: unsafeFEN("2r3k1/1q1nbppp/r3p3/3pP3/pPpP4/P1Q2N2/2RN1PPP/2R4K b - b3 0 23"),
postPos: unsafeFEN("2r3k1/1q1nbppp/r3p3/3pP3/11pP4/PpQ2N2/2RN1PPP/2R4K w - - 0 24"),
},
{
m: &Move{s1: E1, s2: G1, tags: KingSideCastle},
pos: unsafeFEN("r2qk2r/pp1n1ppp/2pbpn2/3p4/2PP4/1PNQPN2/P4PPP/R1B1K2R w KQkq - 1 9"),
postPos: unsafeFEN("r2qk2r/pp1n1ppp/2pbpn2/3p4/2PP4/1PNQPN2/P4PPP/R1B2RK1 b kq - 0 9"),
},
}
)
func unsafeFEN(s string) *Position {
pos, err := decodeFEN(s)
if err != nil {
log.Fatal(err)
}
return pos
}
func TestValidMoves(t *testing.T) {
for _, mt := range validMoves {
if !moveIsValid(mt.pos, mt.m, false) {
log.Println(mt.pos.String())
log.Println(mt.pos.board.Draw())
log.Println(mt.pos.ValidMoves())
log.Println("In Check:", squaresAreAttacked(mt.pos, mt.pos.board.whiteKingSq))
// log.Println("In Check:", mt.pos.inCheck())
mt.pos.turn = mt.pos.turn.Other()
t.Fatalf("expected move %s to be valid", mt.m)
}
}
}
func TestInvalidMoves(t *testing.T) {
for _, mt := range invalidMoves {
if moveIsValid(mt.pos, mt.m, false) {
log.Println(mt.pos.String())
log.Println(mt.pos.board.Draw())
t.Fatalf("expected move %s to be invalid", mt.m)
}
}
}
func TestPositionUpdates(t *testing.T) {
for _, mt := range positionUpdates {
if !moveIsValid(mt.pos, mt.m, true) {
log.Println(mt.pos.String())
log.Println(mt.pos.board.Draw())
log.Println(mt.pos.ValidMoves())
t.Fatalf("expected move %s %v to be valid", mt.m, mt.m.tags)
}
postPos := mt.pos.Update(mt.m)
if postPos.String() != mt.postPos.String() {
t.Fatalf("starting from board \n%s%s\n after move %s\n expected board to be %s\n%s\n but was %s\n%s\n",
mt.pos.String(),
mt.pos.board.Draw(),
mt.m.String(),
mt.postPos.String(),
mt.postPos.board.Draw(),
postPos.String(),
postPos.board.Draw(),
)
}
}
}
type perfTest struct {
pos *Position
nodesPerDepth []int
}
/* https://www.chessprogramming.org/Perft_Results */
var perfResults = []perfTest{
{pos: unsafeFEN("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"), nodesPerDepth: []int{
20, 400, 8902, 197281,
// 4865609, 119060324, 3195901860, 84998978956, 2439530234167, 69352859712417
}},
{pos: unsafeFEN("r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R w KQkq - 0 1"), nodesPerDepth: []int{
48, 2039, 97862,
// 4085603, 193690690
}},
{pos: unsafeFEN("8/2p5/3p4/KP5r/1R3p1k/8/4P1P1/8 w - - 0 1"), nodesPerDepth: []int{
14, 191, 2812, 43238, 674624,
// 11030083, 178633661
}},
{pos: unsafeFEN("r3k2r/Pppp1ppp/1b3nbN/nP6/BBP1P3/q4N2/Pp1P2PP/R2Q1RK1 w kq - 0 1"), nodesPerDepth: []int{
6, 264, 9467, 422333,
// 15833292, 706045033
}},
{pos: unsafeFEN("r2q1rk1/pP1p2pp/Q4n2/bbp1p3/Np6/1B3NBn/pPPP1PPP/R3K2R b KQ - 0 1"), nodesPerDepth: []int{
6, 264, 9467, 422333,
// 15833292, 706045033
}},
{pos: unsafeFEN("rnbq1k1r/pp1Pbppp/2p5/8/2B5/8/PPP1NnPP/RNBQK2R w KQ - 1 8"), nodesPerDepth: []int{
44, 1486, 62379,
// 2103487, 89941194
}},
{pos: unsafeFEN("r4rk1/1pp1qppp/p1np1n2/2b1p1B1/2B1P1b1/P1NP1N2/1PP1QPPP/R4RK1 w - - 0 10"), nodesPerDepth: []int{
46, 2079, 89890,
// 3894594, 164075551, 6923051137, 287188994746, 11923589843526, 490154852788714
}},
}
func TestPerfResults(t *testing.T) {
for _, perf := range perfResults {
countMoves(t, perf.pos, []*Position{perf.pos}, perf.nodesPerDepth, len(perf.nodesPerDepth))
}
}
func countMoves(t *testing.T, originalPosition *Position, positions []*Position, nodesPerDepth []int, maxDepth int) {
if len(nodesPerDepth) == 0 {
return
}
depth := maxDepth - len(nodesPerDepth) + 1
expNodes := nodesPerDepth[0]
newPositions := make([]*Position, 0)
for _, pos := range positions {
for _, move := range pos.ValidMoves() {
newPos := pos.Update(move)
newPositions = append(newPositions, newPos)
}
}
gotNodes := len(newPositions)
if expNodes != gotNodes {
t.Errorf("Depth: %d Expected: %d Got: %d", depth, expNodes, gotNodes)
t.Log("##############################")
t.Log("# Original position info")
t.Log("###")
t.Log(originalPosition.String())
t.Log(originalPosition.board.Draw())
t.Log("##############################")
t.Log("# Details in JSONL (http://jsonlines.org)")
t.Log("###")
for _, pos := range positions {
t.Logf(`{"position": "%s", "moves": %d}`, pos.String(), len(pos.ValidMoves()))
}
}
countMoves(t, originalPosition, newPositions, nodesPerDepth[1:], maxDepth)
}
func BenchmarkValidMoves(b *testing.B) {
pos := unsafeFEN("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1")
b.ResetTimer()
for n := 0; n < b.N; n++ {
pos.ValidMoves()
pos.validMoves = nil
}
}
func moveIsValid(pos *Position, m *Move, useTags bool) bool {
for _, move := range pos.ValidMoves() {
if move.s1 == m.s1 && move.s2 == m.s2 && move.promo == m.promo {
if useTags {
if m.tags != move.tags {
return false
}
}
return true
}
}
return false
}

224
notation.go

@ -0,0 +1,224 @@ @@ -0,0 +1,224 @@
package chess
import (
"fmt"
"strings"
)
// Encoder is the interface implemented by objects that can
// encode a move into a string given the position. It is not
// the encoders responsibility to validate the move.
type Encoder interface {
Encode(pos *Position, m *Move) string
}
// Decoder is the interface implemented by objects that can
// decode a string into a move given the position. It is not
// the decoders responsibility to validate the move. An error
// is returned if the string could not be decoded.
type Decoder interface {
Decode(pos *Position, s string) (*Move, error)
}
// Notation is the interface implemented by objects that can
// encode and decode moves.
type Notation interface {
Encoder
Decoder
}
// LongAlgebraicNotation is a more computer friendly alternative to algebraic
// notation. This notation uses the same format as the UCI (Universal Chess
// Interface). Examples: e2e4, e7e5, e1g1 (white short castling), e7e8q (for promotion)
type LongAlgebraicNotation struct{}
// String implements the fmt.Stringer interface and returns
// the notation's name.
func (_ LongAlgebraicNotation) String() string {
return "Long Algebraic Notation"
}
// Encode implements the Encoder interface.
func (_ LongAlgebraicNotation) Encode(pos *Position, m *Move) string {
return m.S1().String() + m.S2().String() + m.Promo().String()
}
// Decode implements the Decoder interface.
func (_ LongAlgebraicNotation) Decode(pos *Position, s string) (*Move, error) {
l := len(s)
err := fmt.Errorf(`chess: failed to decode long algebraic notation text "%s" for position %s`, s, pos.String())
if l < 4 || l > 5 {
return nil, err
}
s1, ok := strToSquareMap[s[0:2]]
if !ok {
return nil, err
}
s2, ok := strToSquareMap[s[2:4]]
if !ok {
return nil, err
}
promo := NoPieceType
if l == 5 {
promo = pieceTypeFromChar(s[4:5])
if promo == NoPieceType {
return nil, err
}
}
m := &Move{s1: s1, s2: s2, promo: promo}
p := pos.Board().Piece(s1)
if p.Type() == King {
if (s1 == E1 && s2 == G1) || (s1 == E8 && s2 == G8) {
m.addTag(KingSideCastle)
} else if (s1 == E1 && s2 == C1) || (s1 == E8 && s2 == C8) {
m.addTag(QueenSideCastle)
}
} else if p.Type() == Pawn && s2 == pos.enPassantSquare {
m.addTag(EnPassant)
m.addTag(Capture)
}
c1 := p.Color()
c2 := pos.Board().Piece(s2).Color()
if c2 != NoColor && c1 != c2 {
m.addTag(Capture)
}
return m, nil
}
// AlgebraicNotation (or Standard Algebraic Notation) is the
// official chess notation used by FIDE. Examples: e2, e5,
// O-O (short castling), e8=Q (promotion)
type AlgebraicNotation struct{}
// String implements the fmt.Stringer interface and returns
// the notation's name.
func (_ AlgebraicNotation) String() string {
return "Algebraic Notation"
}
// Encode implements the Encoder interface.
func (_ AlgebraicNotation) Encode(pos *Position, m *Move) string {
checkChar := getCheckChar(pos, m)
if m.HasTag(KingSideCastle) {
return "O-O" + checkChar
} else if m.HasTag(QueenSideCastle) {
return "O-O-O" + checkChar
}
p := pos.Board().Piece(m.S1())
pChar := charFromPieceType(p.Type())
s1Str := formS1(pos, m)
capChar := ""
if m.HasTag(Capture) || m.HasTag(EnPassant) {
capChar = "x"
if p.Type() == Pawn && s1Str == "" {
capChar = m.s1.File().String() + "x"
}
}
promoText := charForPromo(m.promo)
return pChar + s1Str + capChar + m.s2.String() + promoText + checkChar
}
// Decode implements the Decoder interface.
func (_ AlgebraicNotation) Decode(pos *Position, s string) (*Move, error) {
s = removeSubstrings(s, "?", "!", "+", "#", "e.p.")
for _, m := range pos.ValidMoves() {
str := AlgebraicNotation{}.Encode(pos, m)
str = removeSubstrings(str, "?", "!", "+", "#", "e.p.")
if str == s {
return m, nil
}
}
return nil, fmt.Errorf("chess: could not decode algebraic notation %s for position %s", s, pos.String())
}
func getCheckChar(pos *Position, move *Move) string {
if !move.HasTag(Check) {
return ""
}
nextPos := pos.Update(move)
if nextPos.Status() == Checkmate {
return "#"
}
return "+"
}
func formS1(pos *Position, m *Move) string {
p := pos.board.Piece(m.s1)
if p.Type() == Pawn {
return ""
}
var req, fileReq, rankReq bool
moves := pos.ValidMoves()
for _, mv := range moves {
if mv.s1 != m.s1 && mv.s2 == m.s2 && p == pos.board.Piece(mv.s1) {
req = true
if mv.s1.File() == m.s1.File() {
rankReq = true;
}
if mv.s1.Rank() == m.s1.Rank() {
fileReq = true
}
}
}
var s1 = ""
if fileReq || !rankReq && req {
s1 = m.s1.File().String()
}
if rankReq {
s1 += m.s1.Rank().String()
}
return s1
}
func charForPromo(p PieceType) string {
c := charFromPieceType(p)
if c != "" {
c = "=" + c
}
return c
}
func charFromPieceType(p PieceType) string {
switch p {
case King:
return "K"
case Queen:
return "Q"
case Rook:
return "R"
case Bishop:
return "B"
case Knight:
return "N"
}
return ""
}
func pieceTypeFromChar(c string) PieceType {
switch c {
case "q":
return Queen
case "r":
return Rook
case "b":
return Bishop
case "n":
return Knight
}
return NoPieceType
}
func removeSubstrings(s string, subs ...string) string {
for _, sub := range subs {
s = strings.Replace(s, sub, "", -1)
}
return s
}

106
notation_test.go

@ -0,0 +1,106 @@ @@ -0,0 +1,106 @@
package chess
import (
"encoding/json"
"os"
"strings"
"testing"
)
type validNotationTest struct {
Pos1 *Position
Pos2 *Position
AlgText string
LongAlgText string
Description string
}
func TestValidDecoding(t *testing.T) {
f, err := os.Open("assets/valid_notation_tests.json")
if err != nil {
t.Fatal(err)
return
}
validTests := []validNotationTest{}
if err := json.NewDecoder(f).Decode(&validTests); err != nil {
t.Fatal(err)
return
}
for _, test := range validTests {
for i, n := range []Notation{AlgebraicNotation{}, LongAlgebraicNotation{}} {
moveText := test.AlgText
if i == 1 {
moveText = test.LongAlgText
}
m, err := n.Decode(test.Pos1, moveText)
if err != nil {
movesStrList := []string{}
for _, m := range test.Pos1.ValidMoves() {
s := n.Encode(test.Pos1, m)
movesStrList = append(movesStrList, s)
}
t.Fatalf("starting from board \n%s\n expected move to be valid error - %s %s\n", test.Pos1.board.Draw(), err, strings.Join(movesStrList, ","))
}
postPos := test.Pos1.Update(m)
if test.Pos2.String() != postPos.String() {
t.Fatalf("starting from board \n%s\n after move %s\n expected board to be %s\n%s\n but was %s\n%s\n",
test.Pos1.board.Draw(), m.String(), test.Pos2.String(),
test.Pos2.board.Draw(), postPos.String(), postPos.board.Draw())
}
}
}
}
type notationDecodeTest struct {
N Notation
Pos *Position
Text string
PostPos *Position
}
var (
invalidDecodeTests = []notationDecodeTest{
{
// opening for white
N: AlgebraicNotation{},
Pos: unsafeFEN("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"),
Text: "e5",
},
{
// http://en.lichess.org/W91M4jms#14
N: AlgebraicNotation{},
Pos: unsafeFEN("rn1qkb1r/pp3ppp/2p1pn2/3p4/2PP4/2NQPN2/PP3PPP/R1B1K2R b KQkq - 0 7"),
Text: "Nd7",
},
{
// http://en.lichess.org/W91M4jms#17
N: AlgebraicNotation{},
Pos: unsafeFEN("r2qk2r/pp1n1ppp/2pbpn2/3p4/2PP4/1PNQPN2/P4PPP/R1B1K2R w KQkq - 1 9"),
Text: "O-O-O-O",
PostPos: unsafeFEN("r2qk2r/pp1n1ppp/2pbpn2/3p4/2PP4/1PNQPN2/P4PPP/R1B2RK1 b kq - 0 9"),
},
{
// http://en.lichess.org/W91M4jms#23
N: AlgebraicNotation{},
Pos: unsafeFEN("3r1rk1/pp1nqppp/2pbpn2/3p4/2PP4/1PNQPN2/PB3PPP/3RR1K1 b - - 5 12"),
Text: "dx4",
},
{
// should not assume pawn for unknown piece type "n"
N: AlgebraicNotation{},
Pos: unsafeFEN("rnbqkbnr/pppp1ppp/8/4p3/4P3/8/PPPP1PPP/RNBQKBNR w KQkq e6 0 2"),
Text: "nf3",
},
}
)
func TestInvalidDecoding(t *testing.T) {
for _, test := range invalidDecodeTests {
if _, err := test.N.Decode(test.Pos, test.Text); err == nil {
t.Fatalf("starting from board\n%s\n expected move notation %s to be invalid", test.Pos.board.Draw(), test.Text)
}
}
}

167
pgn.go

@ -0,0 +1,167 @@ @@ -0,0 +1,167 @@
package chess
import (
"bufio"
"fmt"
"io"
"log"
"regexp"
"strings"
)
// GamesFromPGN returns all PGN decoding games from the
// reader. It is designed to be used decoding multiple PGNs
// in the same file. An error is returned if there is an
// issue parsing the PGNs.
func GamesFromPGN(r io.Reader) ([]*Game, error) {
games := []*Game{}
current := ""
count := 0
totalCount := 0
br := bufio.NewReader(r)
for {
line, err := br.ReadString('\n')
if err == io.EOF {
break
} else if err != nil {
return nil, err
}
if strings.TrimSpace(line) == "" {
count++
} else {
current += line
}
if count == 2 {
game, err := decodePGN(current)
if err != nil {
return nil, err
}
games = append(games, game)
count = 0
current = ""
totalCount++
log.Println("Processed game", totalCount)
}
}
return games, nil
}
func decodePGN(pgn string) (*Game, error) {
tagPairs := getTagPairs(pgn)
moveStrs, outcome := moveList(pgn)
gameFuncs := []func(*Game){}
for _, tp := range tagPairs {
if strings.ToLower(tp.Key) == "fen" {
fenFunc, err := FEN(tp.Value)
if err != nil {
return nil, fmt.Errorf("chess: pgn decode error %s on tag %s", err.Error(), tp.Key)
}
gameFuncs = append(gameFuncs, fenFunc)
break
}
}
gameFuncs = append(gameFuncs, TagPairs(tagPairs))
g := NewGame(gameFuncs...)
g.IgnoreAutomaticDraws = true
var notation Notation = AlgebraicNotation{}
if len(moveStrs) > 0 {
_, err := LongAlgebraicNotation{}.Decode(g.Position(), moveStrs[0])
if err == nil {
notation = LongAlgebraicNotation{}
}
}
for _, alg := range moveStrs {
m, err := notation.Decode(g.Position(), alg)
if err != nil {
return nil, fmt.Errorf("chess: pgn decode error %s on move %d", err.Error(), g.Position().moveCount)
}
if err := g.Move(m); err != nil {
return nil, fmt.Errorf("chess: pgn invalid move error %s on move %d", err.Error(), g.Position().moveCount)
}
}
g.Outcome = outcome
return g, nil
}
func encodePGN(g *Game) string {
s := ""
for _, tag := range g.TagPairs {
s += fmt.Sprintf("[%s \"%s\"]\n", tag.Key, tag.Value)
}
s += "\n"
for i, move := range g.Moves {
pos := g.Positions[i]
txt := g.Notation.Encode(pos, move)
if i%2 == 0 {
s += fmt.Sprintf("%d.%s", (i/2)+1, txt)
} else {
s += fmt.Sprintf(" %s ", txt)
}
}
s += " " + string(g.Outcome)
return s
}
var (
tagPairRegex = regexp.MustCompile(`\[(.*)\s\"(.*)\"\]`)
)
func getTagPairs(pgn string) []*TagPair {
tagPairs := []*TagPair{}
matches := tagPairRegex.FindAllString(pgn, -1)
for _, m := range matches {
results := tagPairRegex.FindStringSubmatch(m)
if len(results) == 3 {
pair := &TagPair{
Key: results[1],
Value: results[2],
}
tagPairs = append(tagPairs, pair)
}
}
return tagPairs
}
var (
moveNumRegex = regexp.MustCompile(`(?:\d+\.+)?(.*)`)
)
func moveList(pgn string) ([]string, Outcome) {
// remove comments
text := removeSection("{", "}", pgn)
// remove variations
text = removeSection(`\(`, `\)`, text)
// remove tag pairs
text = removeSection(`\[`, `\]`, text)
// remove line breaks
text = strings.Replace(text, "\n", " ", -1)
list := strings.Split(text, " ")
filtered := []string{}
var outcome Outcome
for _, move := range list {
move = strings.TrimSpace(move)
switch move {
case string(NoOutcome), string(WhiteWon), string(BlackWon), string(Draw):
outcome = Outcome(move)
case "":
default:
results := moveNumRegex.FindStringSubmatch(move)
if len(results) == 2 && results[1] != "" {
filtered = append(filtered, results[1])
}
}
}
return filtered, outcome
}
func removeSection(leftChar, rightChar, s string) string {
r := regexp.MustCompile(leftChar + ".*?" + rightChar)
for {
i := r.FindStringIndex(s)
if i == nil {
return s
}
s = s[0:i[0]] + s[i[1]:len(s)]
}
}

114
pgn_test.go

@ -0,0 +1,114 @@ @@ -0,0 +1,114 @@
package chess
import (
"strings"
"testing"
)
type pgnTest struct {
PostPos *Position
PGN string
}
var (
validPGNs = []pgnTest{
{
PostPos: unsafeFEN("4r3/6P1/2p2P1k/1p6/pP2p1R1/P1B5/2P2K2/3r4 b - - 0 45"),
PGN: `[Event "?"]
[Site "?"]
[Date "1997.05.03"]
[Round "1"]
[White "Kasparov"]
[Black "Deep-Blue"]
[Result "1-0"]
[WhiteElo "2795"]
1. Nf3 d5 2. g3 Bg4 3. b3 Nd7 4. Bb2 e6 5. Bg2 Ngf6 6. O-O c6
7. d3 Bd6 8. Nbd2 O-O 9. h3 Bh5 10. e3 h6 11. Qe1 Qa5 12. a3
Bc7 13. Nh4 g5 14. Nhf3 e5 15. e4 Rfe8 16. Nh2 Qb6 17. Qc1 a5
18. Re1 Bd6 19. Ndf1 dxe4 20. dxe4 Bc5 21. Ne3 Rad8 22. Nhf1 g4
23. hxg4 Nxg4 24. f3 Nxe3 25. Nxe3 Be7 26. Kh1 Bg5 27. Re2 a4
28. b4 f5 29. exf5 e4 30. f4 Bxe2 31. fxg5 Ne5 32. g6 Bf3 33. Bc3
Qb5 34. Qf1 Qxf1+ 35. Rxf1 h5 36. Kg1 Kf8 37. Bh3 b5 38. Kf2 Kg7
39. g4 Kh6 40. Rg1 hxg4 41. Bxg4 Bxg4 42. Nxg4+ Nxg4+ 43. Rxg4
Rd5 44. f6 Rd1 45. g7 1-0`,
},
{
PostPos: unsafeFEN("4r3/6P1/2p2P1k/1p6/pP2p1R1/P1B5/2P2K2/3r4 b - - 0 45"),
PGN: `[Event "?"]
[Site "http://lichess.org/4HXJOtpN"]
[Date "1997.05.03"]
[White "Kasparov (2795)"]
[Black "Deep-Blue"]
[Result "1-0"]
[WhiteElo "?"]
[BlackElo "?"]
[PlyCount "89"]
[Variant "Standard"]
[TimeControl "-"]
[ECO "A07"]
[Opening "King's Indian Attack, General"]
[Termination "Normal"]
[Annotator "lichess.org"]
1. Nf3 d5 2. g3 { King's Indian Attack, General } Bg4 3. b3 Nd7 4. Bb2 e6 5. Bg2 Ngf6 6. O-O c6 7. d3 Bd6 8. Nbd2 O-O 9. h3 Bh5 10. e3 h6 11. Qe1 Qa5 12. a3 Bc7 13. Nh4 g5 14. Nhf3 e5 15. e4 Rfe8 16. Nh2 Qb6 17. Qc1 a5 18. Re1 Bd6 19. Ndf1 dxe4 20. dxe4 Bc5 21. Ne3 Rad8 22. Nhf1 g4 23. hxg4 Nxg4 24. f3 Nxe3 25. Nxe3 Be7 26. Kh1 Bg5 27. Re2 a4 28. b4 f5 29. exf5 e4 30. f4 Bxe2 31. fxg5 Ne5 32. g6 Bf3 33. Bc3 Qb5 34. Qf1 Qxf1+ 35. Rxf1 h5 36. Kg1 Kf8 37. Bh3 b5 38. Kf2 Kg7?! { (0.70 1.52) Inaccuracy. The best move was Rd6. } (38... Rd6 39. Re1 Re7 40. Rg1 Re8 41. g4 h4 42. g5 Kg8 43. Rf1 Kf8 44. Re1 Kg8 45. Rb1 Rdd8 46. Rf1 Kf8 47. Rg1 Kg8 48. Rb1) 39. g4 Kh6?! { (1.42 2.07) Inaccuracy. The best move was h4. } (39... h4 40. g5 Kf8 41. Bg2 Ng4+ 42. Nxg4 Bxg4 43. Ke3 h3 44. Rf4 hxg2 45. Rxg4 Rd1 46. Rxg2 Rf1 47. Rf2 Rg1 48. Bf6 Rh1 49. Kd4) 40. Rg1 hxg4 41. Bxg4 Bxg4 42. Nxg4+ Nxg4+ 43. Rxg4 Rd5 44. f6 Rd1?? { (1.60 8.36) Blunder. The best move was Rf5+. } (44... Rf5+ 45. Ke2 Rg8 46. g7 e3 47. Bd4 Kh7 48. Kxe3 Re8+ 49. Re4 Rxe4+ 50. Kxe4 Rf1 51. Ke5 Kg8 52. Ke6 Rf4 53. c3 Rf1 54. Ke7) 45. g7 { Black resigns } 1-0`,
},
{
PostPos: unsafeFEN("2r2rk1/pp1bBpp1/2np4/2pp2p1/1bP5/1P4P1/P1QPPPBP/3R1RK1 b - - 0 3"),
PGN: `[Event "?"]
[Site "?"]
[Date "????.??.??"]
[Round "?"]
[White "N.N."]
[Black "N.N."]
[Result "1-0"]
[Annotator "T1R"]
[SetUp "1"]
[FEN "2r2rk1/pp1bqpp1/2nppn1p/2p3N1/1bP5/1PN3P1/PBQPPPBP/3R1RK1 w - - 0 1"]
[PlyCount "5"]
[EventType "swiss"]
1. Nd5 exd5 (1... hxg5 2. Nxe7+ Nxe7) 2. Bxf6 hxg5 3. Bxe7 1-0`,
},
}
)
func TestValidPGNs(t *testing.T) {
for _, test := range validPGNs {
game, err := decodePGN(test.PGN)
if err != nil {
t.Fatalf("recieved unexpected pgn error %s", err.Error())
}
if game.Position().String() != test.PostPos.String() {
t.Fatalf("expected board to be \n%s\nFEN:%s\n but got \n%s\n\nFEN:%s\n",
test.PostPos.board.Draw(), test.PostPos.String(),
game.Position().board.Draw(), game.Position().String())
}
}
}
func BenchmarkPGN(b *testing.B) {
pgn := `[Event "?"]
[Site "?"]
[Date "1997.05.03"]
[Round "1"]
[White "Kasparov"]
[Black "Deep-Blue"]
[Result "1-0"]
[WhiteElo "2795"]
1. Nf3 d5 2. g3 Bg4 3. b3 Nd7 4. Bb2 e6 5. Bg2 Ngf6 6. O-O c6
7. d3 Bd6 8. Nbd2 O-O 9. h3 Bh5 10. e3 h6 11. Qe1 Qa5 12. a3
Bc7 13. Nh4 g5 14. Nhf3 e5 15. e4 Rfe8 16. Nh2 Qb6 17. Qc1 a5
18. Re1 Bd6 19. Ndf1 dxe4 20. dxe4 Bc5 21. Ne3 Rad8 22. Nhf1 g4
23. hxg4 Nxg4 24. f3 Nxe3 25. Nxe3 Be7 26. Kh1 Bg5 27. Re2 a4
28. b4 f5 29. exf5 e4 30. f4 Bxe2 31. fxg5 Ne5 32. g6 Bf3 33. Bc3
Qb5 34. Qf1 Qxf1+ 35. Rxf1 h5 36. Kg1 Kf8 37. Bh3 b5 38. Kf2 Kg7
39. g4 Kh6 40. Rg1 hxg4 41. Bxg4 Bxg4 42. Nxg4+ Nxg4+ 43. Rxg4
Rd5 44. f6 Rd1 45. g7 1-0`
for n := 0; n < b.N; n++ {
opt, _ := PGN(strings.NewReader(pgn))
NewGame(opt)
}
}

192
piece.go

@ -0,0 +1,192 @@ @@ -0,0 +1,192 @@
package chess
// Color represents the color of a chess piece.
type Color int8
const (
// NoColor represents no color
NoColor Color = iota
// White represents the color white
White
// Black represents the color black
Black
)
// Other returns the opposie color of the receiver.
func (c Color) Other() Color {
switch c {
case White:
return Black
case Black:
return White
}
return NoColor
}
// String implements the fmt.Stringer interface and returns
// the color's FEN compatible notation.
func (c Color) String() string {
switch c {
case White:
return "w"
case Black:
return "b"
}
return "-"
}
// Name returns a display friendly name.
func (c Color) Name() string {
switch c {
case White:
return "White"
case Black:
return "Black"
}
return "No Color"
}
// PieceType is the type of a piece.
type PieceType int8
const (
// NoPieceType represents a lack of piece type
NoPieceType PieceType = iota
// King represents a king
King
// Queen represents a queen
Queen
// Rook represents a rook
Rook
// Bishop represents a bishop
Bishop
// Knight represents a knight
Knight
// Pawn represents a pawn
Pawn
)
// PieceTypes returns a slice of all piece types.
func PieceTypes() [6]PieceType {
return [6]PieceType{King, Queen, Rook, Bishop, Knight, Pawn}
}
func (p PieceType) String() string {
switch p {
case King:
return "k"
case Queen:
return "q"
case Rook:
return "r"
case Bishop:
return "b"
case Knight:
return "n"
}
return ""
}
func (p PieceType) promotableTo() bool {
switch p {
case Queen, Rook, Bishop, Knight:
return true
}
return false
}
// Piece is a piece type with a color.
type Piece int8
const (
// NoPiece represents no piece
NoPiece Piece = iota
// WhiteKing is a white king
WhiteKing
// WhiteQueen is a white queen
WhiteQueen
// WhiteRook is a white rook
WhiteRook
// WhiteBishop is a white bishop
WhiteBishop
// WhiteKnight is a white knight
WhiteKnight
// WhitePawn is a white pawn
WhitePawn
// BlackKing is a black king
BlackKing
// BlackQueen is a black queen
BlackQueen
// BlackRook is a black rook
BlackRook
// BlackBishop is a black bishop
BlackBishop
// BlackKnight is a black knight
BlackKnight
// BlackPawn is a black pawn
BlackPawn
)
var (
allPieces = []Piece{
WhiteKing, WhiteQueen, WhiteRook, WhiteBishop, WhiteKnight, WhitePawn,
BlackKing, BlackQueen, BlackRook, BlackBishop, BlackKnight, BlackPawn,
}
)
func getPiece(t PieceType, c Color) Piece {
for _, p := range allPieces {
if p.Color() == c && p.Type() == t {
return p
}
}
return NoPiece
}
// Type returns the type of the piece.
func (p Piece) Type() PieceType {
switch p {
case WhiteKing, BlackKing:
return King
case WhiteQueen, BlackQueen:
return Queen
case WhiteRook, BlackRook:
return Rook
case WhiteBishop, BlackBishop:
return Bishop
case WhiteKnight, BlackKnight:
return Knight
case WhitePawn, BlackPawn:
return Pawn
}
return NoPieceType
}
// Color returns the color of the piece.
func (p Piece) Color() Color {
switch p {
case WhiteKing, WhiteQueen, WhiteRook, WhiteBishop, WhiteKnight, WhitePawn:
return White
case BlackKing, BlackQueen, BlackRook, BlackBishop, BlackKnight, BlackPawn:
return Black
}
return NoColor
}
// String implements the fmt.Stringer interface
func (p Piece) String() string {
return pieceUnicodes[int(p)]
}
var (
pieceUnicodes = []string{" ", "♔", "♕", "♖", "♗", "♘", "♙", "♚", "♛", "♜", "♝", "♞", "♟"}
)
func (p Piece) getFENChar() string {
for key, piece := range fenPieceMap {
if piece == p {
return key
}
}
return ""
}

221
position.go

@ -0,0 +1,221 @@ @@ -0,0 +1,221 @@
package chess
import (
"crypto/md5"
"fmt"
"strconv"
"strings"
)
// Side represents a side of the board.
type Side int
const (
// KingSide is the right side of the board from white's perspective.
KingSide Side = iota + 1
// QueenSide is the left side of the board from white's perspective.
QueenSide
)
// CastleRights holds the state of both sides castling abilities.
type CastleRights string
// CanCastle returns true if the given color and side combination
// can castle, otherwise returns false.
func (cr CastleRights) CanCastle(c Color, side Side) bool {
char := "k"
if side == QueenSide {
char = "q"
}
if c == White {
char = strings.ToUpper(char)
}
return strings.Contains(string(cr), char)
}
// String implements the fmt.Stringer interface and returns
// a FEN compatible string. Ex. KQq
func (cr CastleRights) String() string {
return string(cr)
}
// Position represents the state of the game without reguard
// to its outcome. Position is translatable to FEN notation.
type Position struct {
board *Board
turn Color
castleRights CastleRights
enPassantSquare Square
halfMoveClock int
moveCount int
inCheck bool
validMoves []*Move
}
// Update returns a new position resulting from the given move.
// The move itself isn't validated, if validation is needed use
// Game's Move method. This method is more performant for bots that
// rely on the ValidMoves because it skips redundant validation.
func (pos *Position) Update(m *Move) *Position {
moveCount := pos.moveCount
if pos.turn == Black {
moveCount++
}
cr := pos.CastleRights()
ncr := pos.updateCastleRights(m)
p := pos.board.Piece(m.s1)
halfMove := pos.halfMoveClock
if p.Type() == Pawn || m.HasTag(Capture) || cr != ncr {
halfMove = 0
} else {
halfMove++
}
b := pos.board.copy()
b.update(m)
return &Position{
board: b,
turn: pos.turn.Other(),
castleRights: ncr,
enPassantSquare: pos.updateEnPassantSquare(m),
halfMoveClock: halfMove,
moveCount: moveCount,
inCheck: m.HasTag(Check),
}
}
// ValidMoves returns a list of valid moves for the position.
func (pos *Position) ValidMoves() []*Move {
if pos.validMoves != nil {
return append([]*Move(nil), pos.validMoves...)
}
pos.validMoves = engine{}.CalcMoves(pos, false)
return append([]*Move(nil), pos.validMoves...)
}
// Status returns the position's status as one of the outcome methods.
// Possible returns values include Checkmate, Stalemate, and NoMethod.
func (pos *Position) Status() Method {
return engine{}.Status(pos)
}
// Board returns the position's board.
func (pos *Position) Board() *Board {
return pos.board
}
// Turn returns the color to move next.
func (pos *Position) Turn() Color {
return pos.turn
}
// CastleRights returns the castling rights of the position.
func (pos *Position) CastleRights() CastleRights {
return pos.castleRights
}
// String implements the fmt.Stringer interface and returns a
// string with the FEN format: rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1
func (pos *Position) String() string {
b := pos.board.String()
t := pos.turn.String()
c := pos.castleRights.String()
sq := "-"
if pos.enPassantSquare != NoSquare {
sq = pos.enPassantSquare.String()
}
return fmt.Sprintf("%s %s %s %s %d %d", b, t, c, sq, pos.halfMoveClock, pos.moveCount)
}
// Hash returns a unique hash of the position
func (pos *Position) Hash() [16]byte {
sq := "-"
if pos.enPassantSquare != NoSquare {
sq = pos.enPassantSquare.String()
}
s := pos.turn.String() + ":" + pos.castleRights.String() + ":" + sq
for _, p := range allPieces {
bb := pos.board.bbForPiece(p)
s += ":" + strconv.FormatUint(uint64(bb), 16)
}
return md5.Sum([]byte(s))
}
// MarshalText implements the encoding.TextMarshaler interface and
// encodes the position's FEN.
func (pos *Position) MarshalText() (text []byte, err error) {
return []byte(pos.String()), nil
}
// UnmarshalText implements the encoding.TextUnarshaler interface and
// assumes the data is in the FEN format.
func (pos *Position) UnmarshalText(text []byte) error {
cp, err := decodeFEN(string(text))
if err != nil {
return err
}
pos.board = cp.board
pos.turn = cp.turn
pos.castleRights = cp.castleRights
pos.enPassantSquare = cp.enPassantSquare
pos.halfMoveClock = cp.halfMoveClock
pos.moveCount = cp.moveCount
pos.inCheck = isInCheck(cp)
return nil
}
func (pos *Position) copy() *Position {
return &Position{
board: pos.board.copy(),
turn: pos.turn,
castleRights: pos.castleRights,
enPassantSquare: pos.enPassantSquare,
halfMoveClock: pos.halfMoveClock,
moveCount: pos.moveCount,
inCheck: pos.inCheck,
}
}
func (pos *Position) updateCastleRights(m *Move) CastleRights {
cr := string(pos.castleRights)
p := pos.board.Piece(m.s1)
if p == WhiteKing || m.s1 == H1 || m.s2 == H1 {
cr = strings.Replace(cr, "K", "", -1)
}
if p == WhiteKing || m.s1 == A1 || m.s2 == A1 {
cr = strings.Replace(cr, "Q", "", -1)
}
if p == BlackKing || m.s1 == H8 || m.s2 == H8 {
cr = strings.Replace(cr, "k", "", -1)
}
if p == BlackKing || m.s1 == A8 || m.s2 == A8 {
cr = strings.Replace(cr, "q", "", -1)
}
if cr == "" {
cr = "-"
}
return CastleRights(cr)
}
func (pos *Position) updateEnPassantSquare(m *Move) Square {
p := pos.board.Piece(m.s1)
if p.Type() != Pawn {
return NoSquare
}
if pos.turn == White &&
(bbForSquare(m.s1)&bbRank2) != 0 &&
(bbForSquare(m.s2)&bbRank4) != 0 {
return Square(m.s2 - 8)
} else if pos.turn == Black &&
(bbForSquare(m.s1)&bbRank7) != 0 &&
(bbForSquare(m.s2)&bbRank5) != 0 {
return Square(m.s2 + 8)
}
return NoSquare
}
func (pos *Position) samePosition(pos2 *Position) bool {
return pos.board.String() == pos2.board.String() &&
pos.turn == pos2.turn &&
pos.castleRights.String() == pos2.castleRights.String() &&
pos.enPassantSquare == pos2.enPassantSquare
}

156
square.go

@ -0,0 +1,156 @@ @@ -0,0 +1,156 @@
package chess
const (
numOfSquaresInBoard = 64
numOfSquaresInRow = 8
)
// A Square is one of the 64 rank and file combinations that make up a chess board.
type Square int8
// File returns the square's file.
func (sq Square) File() File {
return File(int(sq) % numOfSquaresInRow)
}
// Rank returns the square's rank.
func (sq Square) Rank() Rank {
return Rank(int(sq) / numOfSquaresInRow)
}
func (sq Square) String() string {
return sq.File().String() + sq.Rank().String()
}
func (sq Square) color() Color {
if ((sq / 8) % 2) == (sq % 2) {
return Black
}
return White
}
func getSquare(f File, r Rank) Square {
return Square((int(r) * 8) + int(f))
}
const (
NoSquare Square = iota - 1
A1
B1
C1
D1
E1
F1
G1
H1
A2
B2
C2
D2
E2
F2
G2
H2
A3
B3
C3
D3
E3
F3
G3
H3
A4
B4
C4
D4
E4
F4
G4
H4
A5
B5
C5
D5
E5
F5
G5
H5
A6
B6
C6
D6
E6
F6
G6
H6
A7
B7
C7
D7
E7
F7
G7
H7
A8
B8
C8
D8
E8
F8
G8
H8
)
const (
fileChars = "abcdefgh"
rankChars = "12345678"
)
// A Rank is the rank of a square.
type Rank int8
const (
Rank1 Rank = iota
Rank2
Rank3
Rank4
Rank5
Rank6
Rank7
Rank8
)
func (r Rank) String() string {
return rankChars[r : r+1]
}
// A File is the file of a square.
type File int8
const (
FileA File = iota
FileB
FileC
FileD
FileE
FileF
FileG
FileH
)
func (f File) String() string {
return fileChars[f : f+1]
}
var (
strToSquareMap = map[string]Square{
"a1": A1, "a2": A2, "a3": A3, "a4": A4, "a5": A5, "a6": A6, "a7": A7, "a8": A8,
"b1": B1, "b2": B2, "b3": B3, "b4": B4, "b5": B5, "b6": B6, "b7": B7, "b8": B8,
"c1": C1, "c2": C2, "c3": C3, "c4": C4, "c5": C5, "c6": C6, "c7": C7, "c8": C8,
"d1": D1, "d2": D2, "d3": D3, "d4": D4, "d5": D5, "d6": D6, "d7": D7, "d8": D8,
"e1": E1, "e2": E2, "e3": E3, "e4": E4, "e5": E5, "e6": E6, "e7": E7, "e8": E8,
"f1": F1, "f2": F2, "f3": F3, "f4": F4, "f5": F5, "f6": F6, "f7": F7, "f8": F8,
"g1": G1, "g2": G2, "g3": G3, "g4": G4, "g5": G5, "g6": G6, "g7": G7, "g8": G8,
"h1": H1, "h2": H2, "h3": H3, "h4": H4, "h5": H5, "h6": H6, "h7": H7, "h8": H8,
}
)

16
stringer.go

@ -0,0 +1,16 @@ @@ -0,0 +1,16 @@
// generated by stringer -type=Method -output=stringer.go; DO NOT EDIT
package chess
import "fmt"
const _Method_name = "NoMethodCheckmateResignationDrawOfferStalemateThreefoldRepetitionFivefoldRepetitionFiftyMoveRuleSeventyFiveMoveRuleInsufficientMaterial"
var _Method_index = [...]uint8{0, 8, 17, 28, 37, 46, 65, 83, 96, 115, 135}
func (i Method) String() string {
if i >= Method(len(_Method_index)-1) {
return fmt.Sprintf("Method(%d)", i)
}
return _Method_name[_Method_index[i]:_Method_index[i+1]]
}

220
vendor/github.com/ajstarks/svgo/LICENSE generated vendored

@ -0,0 +1,220 @@ @@ -0,0 +1,220 @@
Creative Commons Attribution 4.0 International Public License
By exercising the Licensed Rights (defined below), You accept and agree to
be bound by the terms and conditions of this Creative Commons Attribution
4.0 International Public License ("Public License"). To the extent this
Public License may be interpreted as a contract, You are granted the
Licensed Rights in consideration of Your acceptance of these terms and
conditions, and the Licensor grants You such rights in consideration
of benefits the Licensor receives from making the Licensed Material
available under these terms and conditions.
Section 1 – Definitions.
Adapted Material means material subject to Copyright and Similar Rights
that is derived from or based upon the Licensed Material and in which
the Licensed Material is translated, altered, arranged, transformed, or
otherwise modified in a manner requiring permission under the Copyright
and Similar Rights held by the Licensor. For purposes of this Public
License, where the Licensed Material is a musical work, performance,
or sound recording, Adapted Material is always produced where the
Licensed Material is synched in timed relation with a moving image.
Adapter's License means the license You apply to Your Copyright and
Similar Rights in Your contributions to Adapted Material in accordance
with the terms and conditions of this Public License. Copyright and
Similar Rights means copyright and/or similar rights closely related to
copyright including, without limitation, performance, broadcast, sound
recording, and Sui Generis Database Rights, without regard to how the
rights are labeled or categorized. For purposes of this Public License,
the rights specified in Section 2(b)(1)-(2) are not Copyright and Similar
Rights. Effective Technological Measures means those measures that,
in the absence of proper authority, may not be circumvented under laws
fulfilling obligations under Article 11 of the WIPO Copyright Treaty
adopted on December 20, 1996, and/or similar international agreements.
Exceptions and Limitations means fair use, fair dealing, and/or any other
exception or limitation to Copyright and Similar Rights that applies to
Your use of the Licensed Material. Licensed Material means the artistic
or literary work, database, or other material to which the Licensor
applied this Public License. Licensed Rights means the rights granted
to You subject to the terms and conditions of this Public License, which
are limited to all Copyright and Similar Rights that apply to Your use
of the Licensed Material and that the Licensor has authority to license.
Licensor means the individual(s) or entity(ies) granting rights under
this Public License. Share means to provide material to the public by
any means or process that requires permission under the Licensed Rights,
such as reproduction, public display, public performance, distribution,
dissemination, communication, or importation, and to make material
available to the public including in ways that members of the public
may access the material from a place and at a time individually chosen
by them. Sui Generis Database Rights means rights other than copyright
resulting from Directive 96/9/EC of the European Parliament and of the
Council of 11 March 1996 on the legal protection of databases, as amended
and/or succeeded, as well as other essentially equivalent rights anywhere
in the world. You means the individual or entity exercising the Licensed
Rights under this Public License. Your has a corresponding meaning.
Section 2 – Scope.
License grant. Subject to the terms and conditions of this Public
License, the Licensor hereby grants You a worldwide, royalty-free,
non-sublicensable, non-exclusive, irrevocable license to exercise the
Licensed Rights in the Licensed Material to: reproduce and Share the
Licensed Material, in whole or in part; and produce, reproduce, and
Share Adapted Material. Exceptions and Limitations. For the avoidance
of doubt, where Exceptions and Limitations apply to Your use, this
Public License does not apply, and You do not need to comply with
its terms and conditions. Term. The term of this Public License is
specified in Section 6(a). Media and formats; technical modifications
allowed. The Licensor authorizes You to exercise the Licensed Rights in
all media and formats whether now known or hereafter created, and to make
technical modifications necessary to do so. The Licensor waives and/or
agrees not to assert any right or authority to forbid You from making
technical modifications necessary to exercise the Licensed Rights,
including technical modifications necessary to circumvent Effective
Technological Measures. For purposes of this Public License, simply making
modifications authorized by this Section 2(a)(4) never produces Adapted
Material. Downstream recipients. Offer from the Licensor – Licensed
Material. Every recipient of the Licensed Material automatically receives
an offer from the Licensor to exercise the Licensed Rights under the terms
and conditions of this Public License. No downstream restrictions. You
may not offer or impose any additional or different terms or conditions
on, or apply any Effective Technological Measures to, the Licensed
Material if doing so restricts exercise of the Licensed Rights by any
recipient of the Licensed Material. No endorsement. Nothing in this
Public License constitutes or may be construed as permission to assert
or imply that You are, or that Your use of the Licensed Material is,
connected with, or sponsored, endorsed, or granted official status by,
the Licensor or others designated to receive attribution as provided in
Section 3(a)(1)(A)(i). Other rights.
Moral rights, such as the right of integrity, are not licensed under
this Public License, nor are publicity, privacy, and/or other similar
personality rights; however, to the extent possible, the Licensor waives
and/or agrees not to assert any such rights held by the Licensor to the
limited extent necessary to allow You to exercise the Licensed Rights, but
not otherwise. Patent and trademark rights are not licensed under this
Public License. To the extent possible, the Licensor waives any right
to collect royalties from You for the exercise of the Licensed Rights,
whether directly or through a collecting society under any voluntary or
waivable statutory or compulsory licensing scheme. In all other cases
the Licensor expressly reserves any right to collect such royalties.
Section 3 – License Conditions.
Your exercise of the Licensed Rights is expressly made subject to the
following conditions.
Attribution.
If You Share the Licensed Material (including in modified form), You must:
retain the following if it is supplied by the Licensor with the Licensed
Material: identification of the creator(s) of the Licensed Material and
any others designated to receive attribution, in any reasonable manner
requested by the Licensor (including by pseudonym if designated); a
copyright notice; a notice that refers to this Public License; a notice
that refers to the disclaimer of warranties; a URI or hyperlink to the
Licensed Material to the extent reasonably practicable; indicate if You
modified the Licensed Material and retain an indication of any previous
modifications; and indicate the Licensed Material is licensed under this
Public License, and include the text of, or the URI or hyperlink to,
this Public License. You may satisfy the conditions in Section 3(a)(1)
in any reasonable manner based on the medium, means, and context in which
You Share the Licensed Material. For example, it may be reasonable to
satisfy the conditions by providing a URI or hyperlink to a resource
that includes the required information. If requested by the Licensor,
You must remove any of the information required by Section 3(a)(1)(A)
to the extent reasonably practicable. If You Share Adapted Material You
produce, the Adapter's License You apply must not prevent recipients of
the Adapted Material from complying with this Public License. Section 4
– Sui Generis Database Rights.
Where the Licensed Rights include Sui Generis Database Rights that apply
to Your use of the Licensed Material:
for the avoidance of doubt, Section 2(a)(1) grants You the right to
extract, reuse, reproduce, and Share all or a substantial portion of the
contents of the database; if You include all or a substantial portion of
the database contents in a database in which You have Sui Generis Database
Rights, then the database in which You have Sui Generis Database Rights
(but not its individual contents) is Adapted Material; and You must comply
with the conditions in Section 3(a) if You Share all or a substantial
portion of the contents of the database. For the avoidance of doubt,
this Section 4 supplements and does not replace Your obligations under
this Public License where the Licensed Rights include other Copyright and
Similar Rights. Section 5 – Disclaimer of Warranties and Limitation
of Liability.
Unless otherwise separately undertaken by the Licensor, to the
extent possible, the Licensor offers the Licensed Material as-is and
as-available, and makes no representations or warranties of any kind
concerning the Licensed Material, whether express, implied, statutory,
or other. This includes, without limitation, warranties of title,
merchantability, fitness for a particular purpose, non-infringement,
absence of latent or other defects, accuracy, or the presence or absence
of errors, whether or not known or discoverable. Where disclaimers of
warranties are not allowed in full or in part, this disclaimer may not
apply to You. To the extent possible, in no event will the Licensor
be liable to You on any legal theory (including, without limitation,
negligence) or otherwise for any direct, special, indirect, incidental,
consequential, punitive, exemplary, or other losses, costs, expenses,
or damages arising out of this Public License or use of the Licensed
Material, even if the Licensor has been advised of the possibility of
such losses, costs, expenses, or damages. Where a limitation of liability
is not allowed in full or in part, this limitation may not apply to You.
The disclaimer of warranties and limitation of liability provided above
shall be interpreted in a manner that, to the extent possible, most
closely approximates an absolute disclaimer and waiver of all liability.
Section 6 – Term and Termination.
This Public License applies for the term of the Copyright and Similar
Rights licensed here. However, if You fail to comply with this
Public License, then Your rights under this Public License terminate
automatically. Where Your right to use the Licensed Material has
terminated under Section 6(a), it reinstates:
automatically as of the date the violation is cured, provided it is
cured within 30 days of Your discovery of the violation; or upon express
reinstatement by the Licensor. For the avoidance of doubt, this Section
6(b) does not affect any right the Licensor may have to seek remedies
for Your violations of this Public License. For the avoidance of doubt,
the Licensor may also offer the Licensed Material under separate terms
or conditions or stop distributing the Licensed Material at any time;
however, doing so will not terminate this Public License. Sections 1,
5, 6, 7, and 8 survive termination of this Public License. Section 7
– Other Terms and Conditions.
The Licensor shall not be bound by any additional or different terms or
conditions communicated by You unless expressly agreed. Any arrangements,
understandings, or agreements regarding the Licensed Material not stated
herein are separate from and independent of the terms and conditions of
this Public License. Section 8 – Interpretation.
For the avoidance of doubt, this Public License does not, and shall not be
interpreted to, reduce, limit, restrict, or impose conditions on any use
of the Licensed Material that could lawfully be made without permission
under this Public License. To the extent possible, if any provision of
this Public License is deemed unenforceable, it shall be automatically
reformed to the minimum extent necessary to make it enforceable. If
the provision cannot be reformed, it shall be severed from this Public
License without affecting the enforceability of the remaining terms
and conditions. No term or condition of this Public License will be
waived and no failure to comply consented to unless expressly agreed
to by the Licensor. Nothing in this Public License constitutes or may
be interpreted as a limitation upon, or waiver of, any privileges and
immunities that apply to the Licensor or You, including from the legal
processes of any jurisdiction or authority. Creative Commons is not
a party to its public licenses. Notwithstanding, Creative Commons may
elect to apply one of its public licenses to material it publishes and
in those instances will be considered the “Licensor.” The text of
the Creative Commons public licenses is dedicated to the public domain
under the CC0 Public Domain Dedication. Except for the limited purpose of
indicating that material is shared under a Creative Commons public license
or as otherwise permitted by the Creative Commons policies published at
creativecommons.org/policies, Creative Commons does not authorize the
use of the trademark “Creative Commons” or any other trademark or
logo of Creative Commons without its prior written consent including,
without limitation, in connection with any unauthorized modifications
to any of its public licenses or any other arrangements, understandings,
or agreements concerning use of licensed material. For the avoidance of
doubt, this paragraph does not form part of the public licenses.
Creative Commons may be contacted at creativecommons.org.

3
vendor/github.com/ajstarks/svgo/LICENSE-link.txt generated vendored

@ -0,0 +1,3 @@ @@ -0,0 +1,3 @@
The contents of this repository are Licensed under
the Creative Commons Attribution 4.0 International license as described in
https://creativecommons.org/licenses/by/4.0/legalcode

727
vendor/github.com/ajstarks/svgo/README.markdown generated vendored

@ -0,0 +1,727 @@ @@ -0,0 +1,727 @@
# SVGo: A Go library for SVG generation #
The library generates SVG as defined by the Scalable Vector Graphics 1.1 Specification (<http://www.w3.org/TR/SVG11/>).
Output goes to the specified io.Writer.
## Supported SVG elements and functions ##
### Shapes, lines, text
circle, ellipse, polygon, polyline, rect (including roundrects), line, text
### Paths
general, arc, cubic and quadratic bezier paths,
### Image and Gradients
image, linearGradient, radialGradient,
### Transforms ###
translate, rotate, scale, skewX, skewY
### Animation ###
animate, animateMotion, animateTranslate, animateRotate, animateScale, animateSkewX, animateSkewY
### Filter Effects
filter, feBlend, feColorMatrix, feColorMatrix, feComponentTransfer, feComposite, feConvolveMatrix, feDiffuseLighting,
feDisplacementMap, feDistantLight, feFlood, feGaussianBlur, feImage, feMerge, feMorphology, feOffset, fePointLight,
feSpecularLighting, feSpotLight,feTile, feTurbulence
### Metadata elements ###
desc, defs, g (style, transform, id), marker, mask, pattern, title, (a)ddress, link, script, use
## Building and Usage ##
See svgdef.[svg|png|pdf] for a graphical view of the function calls
Usage: (assuming GOPATH is set)
go get github.com/ajstarks/svgo
go install github.com/ajstarks/svgo/...
You can use godoc to browse the documentation from the command line:
$ go doc github.com/ajstarks/svgo
a minimal program, to generate SVG to standard output.
package main
import (
"github.com/ajstarks/svgo"
"os"
)
func main() {
width := 500
height := 500
canvas := svg.New(os.Stdout)
canvas.Start(width, height)
canvas.Circle(width/2, height/2, 100)
canvas.Text(width/2, height/2, "Hello, SVG", "text-anchor:middle;font-size:30px;fill:white")
canvas.End()
}
Drawing in a web server: (http://localhost:2003/circle)
package main
import (
"log"
"github.com/ajstarks/svgo"
"net/http"
)
func main() {
http.Handle("/circle", http.HandlerFunc(circle))
err := http.ListenAndServe(":2003", nil)
if err != nil {
log.Fatal("ListenAndServe:", err)
}
}
func circle(w http.ResponseWriter, req *http.Request) {
w.Header().Set("Content-Type", "image/svg+xml")
s := svg.New(w)
s.Start(500, 500)
s.Circle(250, 250, 125, "fill:none;stroke:black")
s.End()
}
You may view the SVG output with a browser that supports SVG (tested on Chrome, Opera, Firefox and Safari), or any other SVG user-agent such as Batik Squiggle.
### Graphics Sketching with SVGo and svgplay ###
Combined with the svgplay command, SVGo can be used to "sketch" with code in a browser.
To use svgplay and SVGo, first go to a directory with your code, and run:
$ svgplay
2014/06/25 22:05:28 ☠ ☠ ☠ Warning: this server allows a client connecting to 127.0.0.1:1999 to execute code on this computer ☠ ☠ ☠
Next open your browser to the svgplay server you just started.
svgplay only listens on localhost, and uses port 1999 (guess which year SVG was first introduced) by default
http://localhost:1999/
Enter your code in the textarea, and when you are ready to run press Shift--Enter. The code will be compiled, with the results
on the right. To update, change the code and repeat. Note that compilation errors are shown in red under the code. In order for svgplay/SVGo to work, make sure that the io.Writer specified with the New function is os.Stdout.
If you want to sketch with an existing file, enter its URL:
http://localhost:1999/foo.go
![SVGplay](https://farm4.staticflickr.com/3859/14322978157_31c0114850.jpg)
### SVGo Papers and presentations ###
* SVGo paper from SVGOpen 2011 <http://www.svgopen.org/2011/papers/34-SVGo_a_Go_Library_for_SVG_generation>
* Programming Pictures with SVGo <https://speakerdeck.com/u/ajstarks/p/programming-pictures-with-svgo>
* SVGo Workshop <https://speakerdeck.com/u/ajstarks/p/svgo-workshop>
### Tutorial Video ###
A video describing how to use the package can be seen on YouTube at <http://www.youtube.com/watch?v=ze6O2Dj5gQ4>
## Package contents ##
* svg.go: Library
* newsvg: Coding template command
* svgdef: Creates a SVG representation of the API
* animate: Animation demo
* am: Animate motion demo
* amt: Animate transformation demo
* android: The Android logo
* bubtrail: Bubble trails
* bulletgraph: Bullet Graphs (via Stephen Few)
* colortab: Display SVG named colors with RGB values
* compx: Component diagrams
* flower: Random "flowers"
* fontcompare: Compare two fonts
* f50: Get 50 photos from Flickr based on a query
* fe: Filter effects
* funnel: Funnel from transparent circles
* gradient: Linear and radial gradients
* html5logo: HTML5 logo with draggable elements
* imfade: Show image fading
* lewitt: Version of Sol Lewitt's Wall Drawing 91
* ltr: Layer Tennis Remixes
* marker: Test markers
* paths: Demonstrate SVG paths
* pattern: Test patterns
* planets: Show the scale of the Solar system
* pmap: Proportion maps
* randcomp: Compare random number generators
* richter: Gerhard Richter's 256 colors
* rl: Random lines (port of a Processing demo)
* skewabc: Skew ABC
* stockproduct: Visualize product and stock prices
* svgopher: SVGo Mascot
* svgplay: SVGo sketching server
* svgplot: Plot data
* svgrid: Compose SVG files in a grid
* tsg: Twitter Search Grid
* tumblrgrid: Tumblr picture grid
* turbulence: Turbulence filter effect
* vismem: Visualize data from files
* webfonts: "Hello, World" with Google Web Fonts
* websvg: Generate SVG as a web server
## Functions and types ##
Many functions use x, y to specify an object's location, and w, h to specify the object's width and height.
Where applicable, a final optional argument specifies the style to be applied to the object.
The style strings follow the SVG standard; name:value pairs delimited by semicolons, or a
series of name="value" pairs. For example: `"fill:none; opacity:0.3"` or `fill="none" opacity="0.3"` (see: <http://www.w3.org/TR/SVG11/styling.html>)
The SVG type:
type SVG struct {
Writer io.Writer
}
Most operations are methods on this type, specifying the destination io.Writer.
The Offcolor type:
type Offcolor struct {
Offset uint8
Color string
Opacity float64
}
is used to specify the offset, color, and opacity of stop colors in linear and radial gradients
The Filterspec type:
type Filterspec struct {
In string
In2 string
Result string
}
is used to specify inputs and results for filter effects
### Structure, Scripting, Metadata, Transformation and Links ###
New(w io.Writer) *SVG
Constructor, Specify the output destination.
Start(w int, h int, attributes ...string)
begin the SVG document with the width w and height h. Optionally add additional elements
(such as additional namespaces or scripting events)
<http://www.w3.org/TR/SVG11/struct.html#SVGElement>
Startview(w, h, minx, miny, vw, vh int)
begin the SVG document with the width w, height h, with a viewBox at minx, miny, vw, vh.
<http://www.w3.org/TR/SVG11/struct.html#SVGElement>
Startunit(w int, h int, unit string, ns ...string)
begin the SVG document, with width and height in the specified units. Optionally add additional elements
(such as additional namespaces or scripting events)
<http://www.w3.org/TR/SVG11/struct.html#SVGElement>
Startpercent(w int, h int, ns ...string)
begin the SVG document, with width and height in percent. Optionally add additional elements
(such as additional namespaces or scripting events)
<http://www.w3.org/TR/SVG11/struct.html#SVGElement>
StartviewUnit(w, h int, unit string, minx, miny, vw, vh int)
begin the SVG document with the width w, height h, in the specified unit, with a viewBox at minx, miny, vw, vh.
<http://www.w3.org/TR/SVG11/struct.html#SVGElement>
End()
end the SVG document
Script(scriptype string, data ...string)
Script defines a script with a specified type, (for example "application/javascript").
if the first variadic argument is a link, use only the link reference.
Otherwise, treat variadic arguments as the text of the script (marked up as CDATA).
if no data is specified, simply close the script element.
<http://www.w3.org/TR/SVG/script.html>
Style(scriptype string, data ...string)
Style defines a script with a specified type, (for example "text/css").
if the first variadic argument is a link, use only the link reference.
Otherwise, treat variadic arguments as the text of the script (marked up as CDATA).
if no data is specified, simply close the style element.
<https://www.w3.org/TR/SVG/styling.html#StyleElement>
Group(s ...string)
begin a group, with arbitrary attributes
<http://www.w3.org/TR/SVG11/struct.html#GElement>
Gstyle(s string)
begin a group, with the specified style.
<http://www.w3.org/TR/SVG11/struct.html#GElement>
Gid(s string)
begin a group, with the specified id.
Gtransform(s string)
begin a group, with the specified transform, end with Gend().
<http://www.w3.org/TR/SVG11/coords.html#TransformAttribute>
Translate(x, y int)
begins coordinate translation to (x,y), end with Gend().
<http://www.w3.org/TR/SVG11/coords.html#TransformAttribute>
Scale(n float64)
scales the coordinate system by n, end with Gend().
<http://www.w3.org/TR/SVG11/coords.html#TransformAttribute>
ScaleXY(x, y float64)
scales the coordinate system by x, y. End with Gend().
<http://www.w3.org/TR/SVG11/coords.html#TransformAttribute>
SkewX(a float64)
SkewX skews the x coordinate system by angle a, end with Gend().
<http://www.w3.org/TR/SVG11/coords.html#TransformAttribute>
SkewY(a float64)
SkewY skews the y coordinate system by angle a, end with Gend().
<http://www.w3.org/TR/SVG11/coords.html#TransformAttribute>
SkewXY(ax, ay float64)
SkewXY skews x and y coordinate systems by ax, ay respectively, end with Gend().
<http://www.w3.org/TR/SVG11/coords.html#TransformAttribute>
Rotate(r float64)
rotates the coordinate system by r degrees, end with Gend().
<http://www.w3.org/TR/SVG11/coords.html#TransformAttribute>
TranslateRotate(x, y int, r float64)
translates the coordinate system to (x,y), then rotates to r degrees, end with Gend().
RotateTranslate(x, y int, r float64)
rotates the coordinate system r degrees, then translates to (x,y), end with Gend().
Gend()
end the group (must be paired with Gstyle, Gtransform, Gid).
ClipPath(s ...string)
Begin a ClipPath
<http://www.w3.org/TR/SVG/masking.html#ClippingPaths>
ClipEnd()
End a ClipPath
<http://www.w3.org/TR/SVG/masking.html#ClippingPaths>
Def()
begin a definition block.
<http://www.w3.org/TR/SVG11/struct.html#DefsElement>
DefEnd()
end a definition block.
Marker(id string, x, y, w, h int, s ...string)
define a marker
<http://www.w3.org/TR/SVG11/painting.html#MarkerElement>
MarkerEnd()
end a marker
Mask(id string, x int, y int, w int, h int, s ...string)
creates a mask with a specified id, dimension, and optional style.
<http://www.w3.org/TR/SVG/masking.html>
MaskEnd()
ends the Mask element.
Pattern(id string, x, y, width, height int, putype string, s ...string)
define a Pattern with the specified dimensions, the putype can be either "user" or "obj", which sets the patternUnits
attribute to be either userSpaceOnUse or objectBoundingBox.
<http://www.w3.org/TR/SVG11/pservers.html#Patterns>
Desc(s string)
specify the text of the description.
<http://www.w3.org/TR/SVG11/struct.html#DescElement>
Title(s string)
specify the text of the title.
<http://www.w3.org/TR/SVG11/struct.html#TitleElement>
Link(href string, title string)
begin a link named "href", with the specified title.
<http://www.w3.org/TR/SVG11/linking.html#Links>
LinkEnd()
end the link.
Use(x int, y int, link string, s ...string)
place the object referenced at link at the location x, y.
<http://www.w3.org/TR/SVG11/struct.html#UseElement>
### Shapes ###
Circle(x int, y int, r int, s ...string)
draw a circle, centered at x,y with radius r.
<http://www.w3.org/TR/SVG11/shapes.html#CircleElement>
![Circle](http://farm5.static.flickr.com/4144/5187953823_01a1741489_m.jpg)
Ellipse(x int, y int, w int, h int, s ...string)
draw an ellipse, centered at x,y with radii w, and h.
<http://www.w3.org/TR/SVG11/shapes.html#EllipseElement>
![Ellipse](http://farm2.static.flickr.com/1271/5187953773_a9d1fc406c_m.jpg)
Polygon(x []int, y []int, s ...string)
draw a series of line segments using an array of x, y coordinates.
<http://www.w3.org/TR/SVG11/shapes.html#PolygonElement>
![Polygon](http://farm2.static.flickr.com/1006/5187953873_337dc26597_m.jpg)
Rect(x int, y int, w int, h int, s ...string)
draw a rectangle with upper left-hand corner at x,y, with width w, and height h.
<http://www.w3.org/TR/SVG11/shapes.html#RectElement>
![Rect](http://farm2.static.flickr.com/1233/5188556032_86c90e354b_m.jpg)
CenterRect(x int, y int, w int, h int, s ...string)
draw a rectangle with its center at x,y, with width w, and height h.
Roundrect(x int, y int, w int, h int, rx int, ry int, s ...string)
draw a rounded rectangle with upper the left-hand corner at x,y,
with width w, and height h. The radii for the rounded portion
is specified by rx (width), and ry (height).
![Roundrect](http://farm2.static.flickr.com/1275/5188556120_e2a9998fee_m.jpg)
Square(x int, y int, s int, style ...string)
draw a square with upper left corner at x,y with sides of length s.
![Square](http://farm5.static.flickr.com/4110/5187953659_54dcce242e_m.jpg)
### Paths ###
Path(p string, s ...style)
draw the arbitrary path as specified in p, according to the style specified in s. <http://www.w3.org/TR/SVG11/paths.html>
Arc(sx int, sy int, ax int, ay int, r int, large bool, sweep bool, ex int, ey int, s ...string)
draw an elliptical arc beginning coordinate at sx,sy, ending coordinate at ex, ey
width and height of the arc are specified by ax, ay, the x axis rotation is r
if sweep is true, then the arc will be drawn in a "positive-angle" direction (clockwise),
if false, the arc is drawn counterclockwise.
if large is true, the arc sweep angle is greater than or equal to 180 degrees,
otherwise the arc sweep is less than 180 degrees.
<http://www.w3.org/TR/SVG11/paths.html#PathDataEllipticalArcCommands>
![Arc](http://farm2.static.flickr.com/1300/5188556148_df1a176074_m.jpg)
Bezier(sx int, sy int, cx int, cy int, px int, py int, ex int, ey int, s ...string)
draw a cubic bezier curve, beginning at sx,sy, ending at ex,ey
with control points at cx,cy and px,py.
<http://www.w3.org/TR/SVG11/paths.html#PathDataCubicBezierCommands>
![Bezier](http://farm2.static.flickr.com/1233/5188556246_a03e67d013.jpg)
Qbezier(sx int, sy int, cx int, cy int, ex int, ey int, tx int, ty int, s ...string)
draw a quadratic bezier curve, beginning at sx, sy, ending at tx,ty
with control points are at cx,cy, ex,ey.
<http://www.w3.org/TR/SVG11/paths.html#PathDataQuadraticBezierCommands>
![Qbezier](http://farm2.static.flickr.com/1018/5187953917_9a43cf64fb.jpg)
Qbez(sx int, sy int, cx int, cy int, ex int, ey int, s...string)
draws a quadratic bezier curver, with optional style beginning at sx,sy, ending at ex, sy
with the control point at cx, cy.
<http://www.w3.org/TR/SVG11/paths.html#PathDataQuadraticBezierCommands>
![Qbez](http://farm6.static.flickr.com/5176/5569879349_5f726aab5e.jpg)
### Lines ###
Line(x1 int, y1 int, x2 int, y2 int, s ...string)
draw a line segment between x1,y1 and x2,y2.
<http://www.w3.org/TR/SVG11/shapes.html#LineElement>
![Line](http://farm5.static.flickr.com/4154/5188556080_0be19da0bc.jpg)
Polyline(x []int, y []int, s ...string)
draw a polygon using coordinates specified in x,y arrays.
<http://www.w3.org/TR/SVG11/shapes.html#PolylineElement>
![Polyline](http://farm2.static.flickr.com/1266/5188556384_a863273a69.jpg)
### Image and Text ###
Image(x int, y int, w int, h int, link string, s ...string)
place at x,y (upper left hand corner), the image with width w, and height h, referenced at link.
<http://www.w3.org/TR/SVG11/struct.html#ImageElement>
![Image](http://farm5.static.flickr.com/4058/5188556346_e5ce3dcbc2_m.jpg)
Text(x int, y int, t string, s ...string)
Place the specified text, t at x,y according to the style specified in s.
<http://www.w3.org/TR/SVG11/text.html#TextElement>
Textlines(x, y int, s []string, size, spacing int, fill, align string)
Places lines of text in s, starting at x,y, at the specified size, fill, and alignment, and spacing.
Textpath(t string, pathid string, s ...string)
places optionally styled text along a previously defined path.
<http://www.w3.org/TR/SVG11/text.html#TextPathElement>
![Image](http://farm4.static.flickr.com/3149/5694580737_4b291df768_m.jpg)
### Color ###
RGB(r int, g int, b int) string
creates a style string for the fill color designated
by the (r)ed, g(reen), (b)lue components.
<http://www.w3.org/TR/css3-color/>
RGBA(r int, g int, b int, a float64) string
as above, but includes the color's opacity as a value
between 0.0 (fully transparent) and 1.0 (opaque).
### Gradients ###
LinearGradient(id string, x1, y1, x2, y2 uint8, sc []Offcolor)
constructs a linear color gradient identified by id,
along the vector defined by (x1,y1), and (x2,y2).
The stop color sequence defined in sc. Coordinates are expressed as percentages.
<http://www.w3.org/TR/SVG11/pservers.html#LinearGradients>
![LinearGradient](http://farm5.static.flickr.com/4153/5187954033_3972f63fa9.jpg)
RadialGradient(id string, cx, cy, r, fx, fy uint8, sc []Offcolor)
constructs a radial color gradient identified by id,
centered at (cx,cy), with a radius of r.
(fx, fy) define the location of the focal point of the light source.
The stop color sequence defined in sc.
Coordinates are expressed as percentages.
<http://www.w3.org/TR/SVG11/pservers.html#RadialGradients>
![RadialGradient](http://farm2.static.flickr.com/1302/5187954065_7ddba7b819.jpg)
### Animation ###
Animate(link, attr string, from, to int, duration float64, repeat int, s ...string)
Animate animates the item referenced by the link, using the specified attribute
The animation starts at coordinate from, terminates at to, and repeats as specified.
Addtional attributes may be added as needed.
<https://www.w3.org/TR/SVG11/animate.html#AnimateElement>
AnimateMotion(link, path string, duration float64, repeat int, s ...string)
AnimateMotion animates the referenced object ```link``` along the specified ```path```
<https://www.w3.org/TR/SVG11/animate.html#AnimateMotionElement>
AnimateTranslate(link string, fx, fy, tx, ty int, duration float64, repeat int, s ...string)
AnimateTranslate animates the translation transformation (link refers to the object to animate, fx, fy are from coordinates, tx, ty are the to coordinates)
<https://www.w3.org/TR/SVG11/animate.html#AnimateTransformElement>
AnimateRotate(link string, fs, fc, fe, ts, tc, te int, duration float64, repeat int, s ...string)
AnimateRotate animates the rotation transformation (link refers to the object to animate, f[s,c,e] are the from start, center, and end angles, t[s,c,e] are the
start, center, and end angles)
<https://www.w3.org/TR/SVG11/animate.html#AnimateTransformElement>
AnimateScale(link string, from, to, duration float64, repeat int, s ...string)
AnimateScale animates the scale transformation (link refers to the object to animate, from and to specify the scaling factor)
<https://www.w3.org/TR/SVG11/animate.html#AnimateTransformElement>
AnimateSkewX(link string, from, to, duration float64, repeat int, s ...string)
AnimateSkewX animates the skewX transformation ((link refers to the object to animate, from and to specify the skew angle)
<https://www.w3.org/TR/SVG11/animate.html#AnimateTransformElement>
AnimateSkewY(link string, from, to, duration float64, repeat int, s ...string)
AnimateSkewY animates the skewY transformation (link refers to the object to animate, and from and to specify the skew angle)
<https://www.w3.org/TR/SVG11/animate.html#AnimateTransformElement>
### Filter Effects ###
Filter(id string, s ...string)
Filter begins a filter set
Standard reference: <http://www.w3.org/TR/SVG11/filters.html#FilterElement>
Fend()
Fend ends a filter set
Standard reference: <http://www.w3.org/TR/SVG11/filters.html#FilterElement>
FeBlend(fs Filterspec, mode string, s ...string)
FeBlend specifies a Blend filter primitive
Standard reference: <http://www.w3.org/TR/SVG11/filters.html#feBlendElement>
FeColorMatrix(fs Filterspec, values [20]float64, s ...string)
FeColorMatrix specifies a color matrix filter primitive, with matrix values
Standard reference: <http://www.w3.org/TR/SVG11/filters.html#feColorMatrixElement>
FeColorMatrixHue(fs Filterspec, value float64, s ...string)
FeColorMatrix specifies a color matrix filter primitive, with hue values
Standard reference: <http://www.w3.org/TR/SVG11/filters.html#feColorMatrixElement>
FeColorMatrixSaturate(fs Filterspec, value float64, s ...string)
FeColorMatrix specifies a color matrix filter primitive, with saturation values
Standard reference: <http://www.w3.org/TR/SVG11/filters.html#feColorMatrixElement>
FeColorMatrixLuminence(fs Filterspec, s ...string)
FeColorMatrix specifies a color matrix filter primitive, with luminence values
Standard reference: <http://www.w3.org/TR/SVG11/filters.html#feColorMatrixElement>
FeComponentTransfer()
FeComponentTransfer begins a feComponent filter Element>
Standard reference: <http://www.w3.org/TR/SVG11/filters.html#feComponentTransferElement>
FeCompEnd()
FeCompEnd ends a feComponent filter Element>
FeComposite(fs Filterspec, operator string, k1, k2, k3, k4 int, s ...string)
FeComposite specifies a feComposite filter primitive
Standard reference: <http://www.w3.org/TR/SVG11/filters.html#feCompositeElement>
FeConvolveMatrix(fs Filterspec, matrix [9]int, s ...string)
FeConvolveMatrix specifies a feConvolveMatrix filter primitive
Standard reference: <http://www.w3.org/TR/SVG11/filters.html#feConvolveMatrixElement>
FeDiffuseLighting(fs Filterspec, scale, constant float64, s ...string)
FeDiffuseLighting specifies a diffuse lighting filter primitive,
a container for light source Element>s, end with DiffuseEnd()
FeDiffEnd()
FeDiffuseEnd ends a diffuse lighting filter primitive container
Standard reference: <http://www.w3.org/TR/SVG11/filters.html#feDiffuseLightingElement>
FeDisplacementMap(fs Filterspec, scale float64, xchannel, ychannel string, s ...string)
FeDisplacementMap specifies a feDisplacementMap filter primitive
Standard reference: <http://www.w3.org/TR/SVG11/filters.html#feDisplacementMapElement>
FeDistantLight(fs Filterspec, azimuth, elevation float64, s ...string)
FeDistantLight specifies a feDistantLight filter primitive
Standard reference: <http://www.w3.org/TR/SVG11/filters.html#feDistantLightElement>
FeFlood(fs Filterspec, color string, opacity float64, s ...string)
FeFlood specifies a flood filter primitive
Standard reference: <http://www.w3.org/TR/SVG11/filters.html#feFloodElement>
FeFuncLinear(channel string, slope, intercept float64)
FeFuncLinear is the linear form of feFunc
Standard reference: <http://www.w3.org/TR/SVG11/filters.html#feComponentTransferElement>
FeFuncGamma(channel, amplitude, exponent, offset float64)
FeFuncGamma is the gamma curve form of feFunc
Standard reference: <http://www.w3.org/TR/SVG11/filters.html#feComponentTransferElement>
FeFuncTable(channel string, tv []float64)
FeFuncGamma is the form of feFunc using a table of values
Standard reference: <http://www.w3.org/TR/SVG11/filters.html#feComponentTransferElement>
FeFuncDiscrete(channel string, tv []float64)
FeFuncGamma is the form of feFunc using discrete values
Standard reference: <http://www.w3.org/TR/SVG11/filters.html#feComponentTransferElement>
FeGaussianBlur(fs Filterspec, stdx, stdy float64, s ...string)
FeGaussianBlur specifies a Gaussian Blur filter primitive
Standard reference: <http://www.w3.org/TR/SVG11/filters.html#feGaussianBlurElement>
FeImage(href string, result string, s ...string)
FeImage specifies a feImage filter primitive
Standard reference: <http://www.w3.org/TR/SVG11/filters.html#feImageElement>
FeMerge(nodes []string, s ...string)
FeMerge specifies a feMerge filter primitive, containing feMerge Element>s
Standard reference: <http://www.w3.org/TR/SVG11/filters.html#feMergeElement>
FeMorphology(fs Filterspec, operator string, xradius, yradius float64, s ...string)
FeMorphologyLight specifies a feMorphologyLight filter primitive
Standard reference: <http://www.w3.org/TR/SVG11/filters.html#feMorphologyElement>
FeOffset(fs Filterspec, dx, dy int, s ...string)
FeOffset specifies the feOffset filter primitive
Standard reference: <http://www.w3.org/TR/SVG11/filters.html#feOffsetElement>
FePointLight(x, y, z float64, s ...string)
FePointLight specifies a fePpointLight filter primitive
Standard reference: <http://www.w3.org/TR/SVG11/filters.html#fePointLightElement>
FeSpecularLighting(fs Filterspec, scale, constant float64, exponent int, color string, s ...string)
FeSpecularLighting specifies a specular lighting filter primitive,
a container for light source elements, end with SpecularEnd()
FeSpecEnd()
FeSpecularEnd ends a specular lighting filter primitive container
Standard reference: <http://www.w3.org/TR/SVG11/filters.html#feSpecularLightingElement>
FeSpotLight(fs Filterspec, x, y, z, px, py, pz float64, s ...string)
FeSpotLight specifies a feSpotLight filter primitive
Standard reference: <http://www.w3.org/TR/SVG11/filters.html#feSpotLightElement>
FeTile(fs Filterspec, in string, s ...string)
FeTile specifies the tile utility filter primitive
Standard reference: <http://www.w3.org/TR/SVG11/filters.html#feTileElement>
FeTurbulence(fs Filterspec, ftype string, bfx, bfy float64, octaves int, seed int64, stitch bool, s ...string)
FeTurbulence specifies a turbulence filter primitive
Standard reference: <http://www.w3.org/TR/SVG11/filters.html#feTurbulenceElement>
### Filter convenience functions (modeled on CSS filter effects) ###
Blur(p float64)
Blur function by standard deviation
Brightness(p float64)
Brightness function (0-100)
Grayscale()
Apply a grayscale filter to the image
HueRotate(a float64)
Rotate Hues (0-360 degrees)
Invert()
Invert the image's colors
Saturate(p float64)
Percent saturation, 0 is grayscale
Sepia()
Apply sepia tone
### Utility ###
Grid(x int, y int, w int, h int, n int, s ...string)
draws a grid of straight lines starting at x,y, with a width w, and height h, and a size of n.
![Grid](http://farm5.static.flickr.com/4133/5190957924_7a31d0db34.jpg)
### Credits ###
Thanks to Jonathan Wright for the io.Writer update.

126
vendor/github.com/ajstarks/svgo/doc.go generated vendored

@ -0,0 +1,126 @@ @@ -0,0 +1,126 @@
/*
Package svg generates SVG as defined by the Scalable Vector Graphics 1.1 Specification (<http://www.w3.org/TR/SVG11/>).
Output goes to the specified io.Writer.
Supported SVG elements and functions
Shapes, lines, text
circle, ellipse, polygon, polyline, rect (including roundrects), line, text
Paths
general, arc, cubic and quadratic bezier paths,
Image and Gradients
image, linearGradient, radialGradient,
Transforms
translate, rotate, scale, skewX, skewY
Filter Effects
filter, feBlend, feColorMatrix, feColorMatrix, feComponentTransfer, feComposite, feConvolveMatrix, feDiffuseLighting,
feDisplacementMap, feDistantLight, feFlood, feGaussianBlur, feImage, feMerge, feMorphology, feOffset, fePointLight,
feSpecularLighting, feSpotLight,feTile, feTurbulence
Metadata elements
desc, defs, g (style, transform, id), mask, marker, pattern, title, (a)ddress, link, script, style, use
Usage: (assuming GOPATH is set)
go get github.com/ajstarks/svgo
go install github.com/ajstarks/svgo/...
You can use godoc to browse the documentation from the command line:
$ godoc github.com/ajstarks/svgo
a minimal program, to generate SVG to standard output.
package main
import (
"github.com/ajstarks/svgo"
"os"
)
func main() {
width := 500
height := 500
canvas := svg.New(os.Stdout)
canvas.Start(width, height)
canvas.Circle(width/2, height/2, 100)
canvas.Text(width/2, height/2, "Hello, SVG", "text-anchor:middle;font-size:30px;fill:white")
canvas.End()
}
Drawing in a web server: (http://localhost:2003/circle)
package main
import (
"log"
"github.com/ajstarks/svgo"
"net/http"
)
func main() {
http.Handle("/circle", http.HandlerFunc(circle))
err := http.ListenAndServe(":2003", nil)
if err != nil {
log.Fatal("ListenAndServe:", err)
}
}
func circle(w http.ResponseWriter, req *http.Request) {
w.Header().Set("Content-Type", "image/svg+xml")
s := svg.New(w)
s.Start(500, 500)
s.Circle(250, 250, 125, "fill:none;stroke:black")
s.End()
}
Functions and types
Many functions use x, y to specify an object's location, and w, h to specify the object's width and height.
Where applicable, a final optional argument specifies the style to be applied to the object.
The style strings follow the SVG standard; name:value pairs delimited by semicolons, or a
series of name="value" pairs. For example: `"fill:none; opacity:0.3"` or `fill="none" opacity="0.3"` (see: <http://www.w3.org/TR/SVG11/styling.html>)
The SVG type:
type SVG struct {
Writer io.Writer
}
Most operations are methods on this type, specifying the destination io.Writer.
The Offcolor type:
type Offcolor struct {
Offset uint8
Color string
Opacity float64
}
is used to specify the offset, color, and opacity of stop colors in linear and radial gradients
The Filterspec type:
type Filterspec struct {
In string
In2 string
Result string
}
is used to specify inputs and results for filter effects
*/
package svg

BIN
vendor/github.com/ajstarks/svgo/gophercolor128x128.png generated vendored

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

39
vendor/github.com/ajstarks/svgo/newsvg generated vendored

@ -0,0 +1,39 @@ @@ -0,0 +1,39 @@
#!/bin/sh
if test $# -lt 1
then
echo "specify a file"
exit 2
fi
if test ! -f $1
then
cat <<! > $1
package main
import (
"github.com/ajstarks/svgo"
"os"
)
var (
width = 500
height = 500
canvas = svg.New(os.Stdout)
)
func background(v int) { canvas.Rect(0, 0, width, height, canvas.RGB(v, v, v)) }
func main() {
canvas.Start(width, height)
background(255)
// your code here
canvas.Grid(0, 0, width, height, 10, "stroke:black;opacity:0.1")
canvas.End()
}
!
fi
$EDITOR $1

1055
vendor/github.com/ajstarks/svgo/svg.go generated vendored

File diff suppressed because it is too large Load Diff

BIN
vendor/github.com/ajstarks/svgo/svgdef.pdf generated vendored

Binary file not shown.

BIN
vendor/github.com/ajstarks/svgo/svgdef.png generated vendored

Binary file not shown.

After

Width:  |  Height:  |  Size: 412 KiB

395
vendor/github.com/ajstarks/svgo/svgdef.svg generated vendored

@ -0,0 +1,395 @@ @@ -0,0 +1,395 @@
<?xml version="1.0"?>
<!-- Generated by SVGo -->
<svg width="4500" height="3375"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink">
<desc>Object Definitions</desc>
<defs>
<linearGradient id="linear" x1="0%" y1="0%" x2="100%" y2="0%">
<stop offset="0%" stop-color="white" stop-opacity="1.00"/>
<stop offset="25%" stop-color="lightblue" stop-opacity="1.00"/>
<stop offset="75%" stop-color="blue" stop-opacity="1.00"/>
<stop offset="100%" stop-color="rgb(0,0,127)" stop-opacity="1.00"/>
</linearGradient>
<radialGradient id="radial" cx="0%" cy="0%" r="100%" fx="50%" fy="50%">
<stop offset="0%" stop-color="white" stop-opacity="1.00"/>
<stop offset="25%" stop-color="lightblue" stop-opacity="1.00"/>
<stop offset="75%" stop-color="blue" stop-opacity="1.00"/>
<stop offset="100%" stop-color="rgb(0,0,127)" stop-opacity="1.00"/>
</radialGradient>
<path d="M 0,0 A62,62 0 0 1 250,0" id="tpath" />
<g id="square">
<circle cx="0" cy="0" r="4" style="fill:rgb(220,220,220)"/>
<text x="0" y="-24" style="fill:gray; text-anchor:middle">x, y</text>
<rect x="0" y="0" width="125" height="125" style="fill:none; stroke-width:2;stroke:rgb(0,0,127)"/>
<text x="62" y="-24" style="fill:gray; text-anchor:middle">w</text>
<text x="62" y="149" style="fill:black; text-anchor:middle;font-size:24px">Square(x, y, w int, style ...string)</text>
</g>
<g id="rect">
<circle cx="0" cy="0" r="4" style="fill:rgb(220,220,220)"/>
<text x="0" y="-24" style="fill:gray; text-anchor:middle">x, y</text>
<rect x="0" y="0" width="250" height="125" style="fill:none; stroke-width:2;stroke:rgb(0,0,127)"/>
<text x="-24" y="62" style="fill:gray; text-anchor:middle">h</text>
<text x="125" y="-24" style="fill:gray; text-anchor:middle">w</text>
<text x="125" y="149" style="fill:black; text-anchor:middle;font-size:24px">Rect(x, y, w, h int, style ...string)</text>
</g>
<g id="crect">
<circle cx="125" cy="62" r="4" style="fill:rgb(220,220,220)"/>
<text x="125" y="38" style="fill:gray; text-anchor:middle">x, y</text>
<rect x="0" y="0" width="250" height="125" style="fill:none; stroke-width:2;stroke:rgb(0,0,127)"/>
<text x="-24" y="62" style="fill:gray; text-anchor:middle">h</text>
<text x="125" y="-24" style="fill:gray; text-anchor:middle">w</text>
<text x="125" y="149" style="fill:black; text-anchor:middle;font-size:24px">CenterRect(x, y, w, h int, style ...string)</text>
</g>
<g id="roundrect">
<circle cx="0" cy="0" r="4" style="fill:rgb(220,220,220)"/>
<text x="0" y="-24" style="fill:gray; text-anchor:middle">x, y</text>
<rect x="0" y="0" width="250" height="125" rx="25" ry="25" style="fill:none; stroke-width:2;stroke:rgb(0,0,127)"/>
<text x="-24" y="62" style="fill:gray; text-anchor:middle">h</text>
<text x="125" y="-24" style="fill:gray; text-anchor:middle">w</text>
<line x1="25" y1="0" x2="25" y2="25" style="stroke:black; stroke-width:1"/>
<line x1="0" y1="25" x2="25" y2="25" style="stroke:black; stroke-width:1"/>
<text x="49" y="13" style="fill:gray; text-anchor:middle">ry</text>
<text x="12" y="49" style="fill:gray; text-anchor:middle">rx</text>
<text x="125" y="149" style="fill:black; text-anchor:middle;font-size:24px">Roundrect(x, y, w, h, rx, ry int, style ...string)</text>
</g>
<g id="polygon">
<circle cx="0" cy="0" r="4" style="fill:rgb(220,220,220)"/>
<text x="0" y="-24" style="fill:gray; text-anchor:middle">x, y</text>
<circle cx="125" cy="-31" r="4" style="fill:rgb(220,220,220)"/>
<text x="125" y="-55" style="fill:gray; text-anchor:middle">x, y</text>
<circle cx="250" cy="0" r="4" style="fill:rgb(220,220,220)"/>
<text x="250" y="-24" style="fill:gray; text-anchor:middle">x, y</text>
<circle cx="250" cy="93" r="4" style="fill:rgb(220,220,220)"/>
<text x="250" y="69" style="fill:gray; text-anchor:middle">x, y</text>
<circle cx="125" cy="62" r="4" style="fill:rgb(220,220,220)"/>
<text x="125" y="38" style="fill:gray; text-anchor:middle">x, y</text>
<circle cx="0" cy="93" r="4" style="fill:rgb(220,220,220)"/>
<text x="0" y="69" style="fill:gray; text-anchor:middle">x, y</text>
<polygon points="0,0 125,-31 250,0 250,93 125,62 0,93" style="fill:none; stroke-width:2;stroke:rgb(0,0,127)"/>
<text x="125" y="149" style="fill:black; text-anchor:middle;font-size:24px">Polygon(x, y []int, style ...string)</text>
</g>
<g id="circle">
<g transform="translate(125,62)">
<circle cx="0" cy="0" r="4" style="fill:rgb(220,220,220)"/>
<text x="0" y="-24" style="fill:gray; text-anchor:middle">x, y</text>
<circle cx="0" cy="0" r="62" style="fill:none; stroke-width:2;stroke:rgb(0,0,127)"/>
<line x1="0" y1="0" x2="62" y2="0" style="stroke:black; stroke-width:1"/>
<text x="31" y="24" style="fill:gray; text-anchor:middle">r</text>
<text x="0" y="86" style="fill:black; text-anchor:middle;font-size:24px">Circle(x, y, r int, style ...string)</text>
</g>
</g>
<g id="ellipse">
<g transform="translate(125,62)">
<circle cx="0" cy="0" r="4" style="fill:rgb(220,220,220)"/>
<text x="0" y="-24" style="fill:gray; text-anchor:middle">x, y</text>
<ellipse cx="0" cy="0" rx="125" ry="62" style="fill:none; stroke-width:2;stroke:rgb(0,0,127)"/>
<line x1="0" y1="0" x2="125" y2="0" style="stroke:black; stroke-width:1"/>
<line x1="0" y1="0" x2="0" y2="62" style="stroke:black; stroke-width:1"/>
<text x="62" y="24" style="fill:gray; text-anchor:middle">rx</text>
<text x="-24" y="31" style="fill:gray; text-anchor:middle">ry</text>
<text x="0" y="86" style="fill:black; text-anchor:middle;font-size:24px">Ellipse(x, y, rx, ry int, style ...string)</text>
</g>
</g>
<g id="line">
<circle cx="0" cy="0" r="4" style="fill:rgb(220,220,220)"/>
<text x="0" y="-24" style="fill:gray; text-anchor:middle">x1, y1</text>
<circle cx="250" cy="0" r="4" style="fill:rgb(220,220,220)"/>
<text x="250" y="-24" style="fill:gray; text-anchor:middle">x2, y2</text>
<line x1="0" y1="0" x2="250" y2="0" style="fill:none; stroke-width:2;stroke:rgb(0,0,127)"/>
<text x="125" y="149" style="fill:black; text-anchor:middle;font-size:24px">Line(x1, y1, x2, y2 int, style ...string)</text>
</g>
<g id="polyline">
<circle cx="0" cy="0" r="4" style="fill:rgb(220,220,220)"/>
<text x="0" y="-24" style="fill:gray; text-anchor:middle">x, y</text>
<circle cx="83" cy="-62" r="4" style="fill:rgb(220,220,220)"/>
<text x="83" y="-86" style="fill:gray; text-anchor:middle">x, y</text>
<circle cx="187" cy="-41" r="4" style="fill:rgb(220,220,220)"/>
<text x="187" y="-65" style="fill:gray; text-anchor:middle">x, y</text>
<circle cx="250" cy="-125" r="4" style="fill:rgb(220,220,220)"/>
<text x="250" y="-149" style="fill:gray; text-anchor:middle">x, y</text>
<polyline points="0,0 83,-62 187,-41 250,-125" style="fill:none; stroke-width:2;stroke:rgb(0,0,127)"/>
<text x="125" y="149" style="fill:black; text-anchor:middle;font-size:24px">Polyline(x, y []int, style ...string)</text>
</g>
<g id="arc">
<circle cx="0" cy="0" r="4" style="fill:rgb(220,220,220)"/>
<text x="0" y="-24" style="fill:gray; text-anchor:middle">sx, sy</text>
<circle cx="250" cy="0" r="4" style="fill:rgb(220,220,220)"/>
<text x="250" y="-24" style="fill:gray; text-anchor:middle">ex, ey</text>
<path d="M0,0 A62,62 0 0 1 250,0" style="fill:none; stroke-width:2;stroke:rgb(0,0,127)"/>
<text x="125" y="148" style="fill:black; text-anchor:middle;font-size:24px">Arc(sx, sy, ax, ay, r int, lflag, sflag bool, ex, ey int, style ...string)</text>
</g>
<g id="path">
<path d="M36,5l12,41l12-41h33v4l-13,21c30,10,2,69-21,28l7-2c15,27,33,-22,3,-19v-4l12-20h-15l-17,59h-1l-13-42l-12,42h-1l-20-67h9l12,41l8-28l-4-13h9" fill="rgb(0,0,127)" />
<path d="M94,53c15,32,30,14,35,7l-1-7c-16,26-32,3-34,0M122,16c-10-21-34,0-21,30c-5-30 16,-38 23,-21l5-10l-2-9" style="fill-opacity:0.50; fill:rgb(0,0,0)"/>
<circle cx="0" cy="0" r="4" style="fill:rgb(220,220,220)"/>
<text x="0" y="-24" style="fill:gray; text-anchor:middle">x, y</text>
<text x="62" y="160" style="fill:black; text-anchor:middle;font-size:24px">Path(s string, style ...string)</text>
</g>
<g id="qbez">
<circle cx="0" cy="0" r="4" style="fill:rgb(220,220,220)"/>
<text x="0" y="-24" style="fill:gray; text-anchor:middle">sx, sy</text>
<circle cx="41" cy="-93" r="4" style="fill:rgb(220,220,220)"/>
<text x="41" y="-117" style="fill:gray; text-anchor:middle">cx, cy</text>
<circle cx="250" cy="0" r="4" style="fill:rgb(220,220,220)"/>
<text x="250" y="-24" style="fill:gray; text-anchor:middle">ex, ey</text>
<path d="M0,0 Q41,-93 250,0" style="fill:none; stroke-width:2;stroke:rgb(0,0,127)"/>
<text x="125" y="149" style="fill:black; text-anchor:middle;font-size:24px">Qbez(sx, sy, cx, cy, ex, ey int, style ...string)</text>
</g>
<g id="bezier">
<circle cx="0" cy="0" r="4" style="fill:rgb(220,220,220)"/>
<text x="0" y="-24" style="fill:gray; text-anchor:middle">sx, sy</text>
<circle cx="125" cy="-62" r="4" style="fill:rgb(220,220,220)"/>
<text x="125" y="-86" style="fill:gray; text-anchor:middle">cx, cy</text>
<circle cx="125" cy="62" r="4" style="fill:rgb(220,220,220)"/>
<text x="125" y="38" style="fill:gray; text-anchor:middle">px, py</text>
<circle cx="250" cy="0" r="4" style="fill:rgb(220,220,220)"/>
<text x="250" y="-24" style="fill:gray; text-anchor:middle">ex, ey</text>
<path d="M0,0 C125,-62 125,62 250,0" style="fill:none; stroke-width:2;stroke:rgb(0,0,127)"/>
<text x="125" y="149" style="fill:black; text-anchor:middle;font-size:24px">Bezier(sx, sy, cx, cy, px, py, ex, ey int, style ...string)</text>
</g>
<g id="image">
<circle cx="0" cy="0" r="4" style="fill:rgb(220,220,220)"/>
<text x="0" y="-24" style="fill:gray; text-anchor:middle">x, y</text>
<rect x="0" y="0" width="128" height="128" style="fill:none; stroke-width:2;stroke:rgb(0,0,127)"/>
<text x="-24" y="64" style="fill:gray; text-anchor:middle">h</text>
<text x="64" y="-24" style="fill:gray; text-anchor:middle">w</text>
<image x="0" y="0" width="128" height="128" xlink:href="gophercolor128x128.png" />
<text x="64" y="152" style="fill:black; text-anchor:middle;font-size:24px">Image(x, y, w, h, int path string, style ...string)</text>
</g>
<g id="lgrad">
<rect x="0" y="0" width="250" height="125" style="fill:url(#linear)"/>
<circle cx="0" cy="0" r="4" style="fill:rgb(220,220,220)"/>
<text x="0" y="-24" style="fill:gray; text-anchor:middle">x1%, y1%</text>
<circle cx="250" cy="0" r="4" style="fill:rgb(220,220,220)"/>
<text x="250" y="-24" style="fill:gray; text-anchor:middle">x2%, y2%</text>
<text x="125" y="149" style="fill:black; text-anchor:middle;font-size:24px">LinearGradient(s string, x1, y1, x2, y2 uint8, oc []Offcolor)</text>
</g>
<g id="rgrad">
<rect x="0" y="0" width="250" height="125" style="fill:url(#radial)"/>
<circle cx="0" cy="0" r="4" style="fill:rgb(220,220,220)"/>
<text x="0" y="-24" style="fill:gray; text-anchor:middle">cx%, cy%</text>
<circle cx="125" cy="62" r="4" style="fill:rgb(220,220,220)"/>
<text x="125" y="38" style="fill:gray; text-anchor:middle">fx%, fy%</text>
<text x="125" y="149" style="fill:black; text-anchor:middle;font-size:24px">RadialGradient(s string, cx, cy, r, fx, fy uint8, oc []Offcolor)</text>
</g>
<g id="trans">
<circle cx="0" cy="0" r="4" style="fill:rgb(220,220,220)"/>
<text x="0" y="-24" style="fill:gray; text-anchor:middle">0, 0</text>
<circle cx="167" cy="0" r="4" style="fill:rgb(220,220,220)"/>
<text x="167" y="-24" style="fill:gray; text-anchor:middle">x, y</text>
<text x="125" y="149" style="fill:black; text-anchor:middle;font-size:24px">Translate(x, y int)</text>
<rect x="0" y="0" width="83" height="125" style="fill:none; stroke-width:2;stroke:rgb(0,0,127)"/>
<g transform="translate(167,0)">
<rect x="0" y="0" width="83" height="125" style="fill-opacity:0.25;fill:rgb(0,0,127)"/>
</g>
</g>
<g id="grid">
<circle cx="0" cy="0" r="4" style="fill:rgb(220,220,220)"/>
<text x="0" y="-24" style="fill:gray; text-anchor:middle">x, y</text>
<text x="-24" y="62" style="fill:gray; text-anchor:middle">h</text>
<text x="125" y="-24" style="fill:gray; text-anchor:middle">w</text>
<text x="55" y="15" style="fill:gray; text-anchor:middle">n</text>
<g style="stroke:rgb(0,0,127)">
<line x1="0" y1="0" x2="0" y2="125" />
<line x1="31" y1="0" x2="31" y2="125" />
<line x1="62" y1="0" x2="62" y2="125" />
<line x1="93" y1="0" x2="93" y2="125" />
<line x1="124" y1="0" x2="124" y2="125" />
<line x1="155" y1="0" x2="155" y2="125" />
<line x1="186" y1="0" x2="186" y2="125" />
<line x1="217" y1="0" x2="217" y2="125" />
<line x1="248" y1="0" x2="248" y2="125" />
<line x1="0" y1="0" x2="250" y2="0" />
<line x1="0" y1="31" x2="250" y2="31" />
<line x1="0" y1="62" x2="250" y2="62" />
<line x1="0" y1="93" x2="250" y2="93" />
<line x1="0" y1="124" x2="250" y2="124" />
</g>
<text x="125" y="149" style="fill:black; text-anchor:middle;font-size:24px">Grid(x, y, w, h, n int, style ...string)</text>
</g>
<g id="text">
<circle cx="0" cy="62" r="4" style="fill:rgb(220,220,220)"/>
<text x="0" y="86" style="fill:gray; text-anchor:middle">x, y</text>
<text x="0" y="62" style="text-anchor:start;font-size:32pt">hello, this is SVG</text>
<text x="125" y="149" style="fill:black; text-anchor:middle;font-size:24px">Text(x, y int, s string, style ...string)</text>
</g>
<g id="scale">
<circle cx="0" cy="0" r="4" style="fill:rgb(220,220,220)"/>
<text x="0" y="-24" style="fill:gray; text-anchor:middle">0, 0</text>
<rect x="0" y="0" width="250" height="125" style="fill:none; stroke-width:2;stroke:rgb(0,0,127)"/>
<g transform="scale(0.5)">
<rect x="0" y="0" width="250" height="125" style="fill-opacity:0.25;fill:rgb(0,0,127)"/>
</g>
<text x="125" y="149" style="fill:black; text-anchor:middle;font-size:24px">Scale(n float64)</text>
</g>
<g id="scalexy">
<circle cx="0" cy="0" r="4" style="fill:rgb(220,220,220)"/>
<text x="0" y="-24" style="fill:gray; text-anchor:middle">0, 0</text>
<rect x="0" y="0" width="250" height="125" style="fill:none; stroke-width:2;stroke:rgb(0,0,127)"/>
<g transform="scale(0.5,0.75)">
<rect x="0" y="0" width="250" height="125" style="fill-opacity:0.25;fill:rgb(0,0,127)"/>
</g>
<text x="125" y="149" style="fill:black; text-anchor:middle;font-size:24px">ScaleXY(x, y float64)</text>
</g>
<g id="skewx">
<circle cx="0" cy="0" r="4" style="fill:rgb(220,220,220)"/>
<text x="0" y="-24" style="fill:gray; text-anchor:middle">0, 0</text>
<rect x="0" y="0" width="250" height="125" style="fill:none; stroke-width:2;stroke:rgb(0,0,127)"/>
<g transform="skewX(30)">
<rect x="0" y="0" width="250" height="125" style="fill-opacity:0.25;fill:rgb(0,0,127)"/>
</g>
<text x="125" y="149" style="fill:black; text-anchor:middle;font-size:24px">SkewX(a float64)</text>
</g>
<g id="skewy">
<circle cx="0" cy="0" r="4" style="fill:rgb(220,220,220)"/>
<text x="0" y="-24" style="fill:gray; text-anchor:middle">0, 0</text>
<rect x="0" y="0" width="250" height="125" style="fill:none; stroke-width:2;stroke:rgb(0,0,127)"/>
<g transform="skewY(10)">
<rect x="0" y="0" width="250" height="125" style="fill-opacity:0.25;fill:rgb(0,0,127)"/>
</g>
<text x="125" y="149" style="fill:black; text-anchor:middle;font-size:24px">SkewY(a float64)</text>
</g>
<g id="skewxy">
<circle cx="0" cy="0" r="4" style="fill:rgb(220,220,220)"/>
<text x="0" y="-24" style="fill:gray; text-anchor:middle">0, 0</text>
<rect x="0" y="0" width="250" height="125" style="fill:none; stroke-width:2;stroke:rgb(0,0,127)"/>
<g transform="skewX(10) skewY(10)">
<rect x="0" y="0" width="250" height="125" style="fill-opacity:0.25;fill:rgb(0,0,127)"/>
</g>
<text x="125" y="149" style="fill:black; text-anchor:middle;font-size:24px">SkewXY(x, y float64)</text>
</g>
<g id="rotate">
<circle cx="0" cy="0" r="4" style="fill:rgb(220,220,220)"/>
<text x="0" y="-24" style="fill:gray; text-anchor:middle">0, 0</text>
<text x="125" y="149" style="fill:black; text-anchor:middle;font-size:24px">Rotate(r float64)</text>
<rect x="0" y="0" width="250" height="125" style="fill:none; stroke-width:2;stroke:rgb(0,0,127)"/>
<path d="M125,0 Q135,31 108,62" style="fill:none;stroke:gray"/>
<text x="62" y="24" style="fill:gray; text-anchor:middle">r</text>
<g transform="rotate(30)">
<rect x="0" y="0" width="250" height="125" style="fill-opacity:0.25;fill:rgb(0,0,127)"/>
</g>
</g>
<g id="textpath">
<text fill="rgb(0,0,127)" text-anchor="start" font-size="16pt" ><textPath xlink:href="#tpath">It&#39;s &#34;fine&#34; &amp; &#34;dandy&#34; to draw text along a path</textPath></text>
<text x="125" y="149" style="fill:black; text-anchor:middle;font-size:24px">Textpath(s, pathid string, style ...string)</text>
</g>
<g id="meta">
<g style="font-size:24px;fill:black;text-anchor:start">
<text x="0" y="24" >New(w io Writer)</text>
<text x="0" y="52" >Start(w, h int, options ...string)/End()</text>
<text x="0" y="80" >Startview(w, h, minx, miny, vw, vh int)</text>
<text x="0" y="108" >Group(s ...string)/Gend()</text>
<text x="0" y="136" >Gstyle(s string)/Gend()</text>
<text x="0" y="164" >Gtransform(s string)/Gend()</text>
<text x="0" y="192" >Gid(id string)/Gend()</text>
<text x="0" y="220" >ClipPath(s ...string)/ClipEnd()</text>
<text x="0" y="248" >Def()/DefEnd()</text>
<text x="0" y="276" >Marker()/MarkerEnd()</text>
<text x="0" y="304" >Pattern()/PatternEnd()</text>
<text x="0" y="332" >Desc(s string)</text>
<text x="0" y="360" >Title(s string)</text>
<text x="0" y="388" >Script(type, data ...string)</text>
<text x="0" y="416" >Mask(id string, x,y,w,h int, style ...string)/MaskEnd()</text>
<text x="0" y="444" >Link(href string, title string)/LinkEnd()</text>
<text x="0" y="472" >Use(x int, y int, link string, style ...string)</text>
</g>
<g style="font-size:24px;fill:rgb(127,127,127);text-anchor:start">
<text x="650" y="24" >specify destination</text>
<text x="650" y="52" >begin/end the document</text>
<text x="650" y="80" >begin/end the document with viewport</text>
<text x="650" y="108" >begin/end group with attributes</text>
<text x="650" y="136" >begin/end group style</text>
<text x="650" y="164" >begin/end group transform</text>
<text x="650" y="192" >begin/end group id</text>
<text x="650" y="220" >begin/end clip path</text>
<text x="650" y="248" >begin/end a defintion block</text>
<text x="650" y="276" >begin/end markers</text>
<text x="650" y="304" >begin/end pattern</text>
<text x="650" y="332" >set the description element</text>
<text x="650" y="360" >set the title element</text>
<text x="650" y="388" >define a script</text>
<text x="650" y="416" >begin/end mask element</text>
<text x="650" y="444" >begin/end link to href, with a title</text>
<text x="650" y="472" >use defined objects</text>
</g>
<text x="500" y="534" style="fill:black; text-anchor:middle;font-size:24px">Textlines(x, y int, s []string, size, spacing int, fill, align string)</text>
</g>
<g id="rgb">
<g style="fill:gray; text-anchor:middle">
<circle cx="62" cy="0" r="15" style="fill:rgb(44,0,0)"/>
<circle cx="125" cy="0" r="15" style="fill:rgb(0,77,0)"/>
<circle cx="187" cy="0" r="15" style="fill:rgb(0,0,232)"/>
<circle cx="250" cy="0" r="15" style="fill:rgb(44,77,232)"/>
<text x="62" y="62" >r</text>
<text x="125" y="62" >g</text>
<text x="187" y="62" >b</text>
<text x="219" y="8" >-&gt;</text>
</g>
<text x="125" y="149" style="fill:black; text-anchor:middle;font-size:24px">RGB(r, g, b int)</text>
</g>
<g id="rgba">
<g style="fill:gray; text-anchor:middle">
<circle cx="62" cy="0" r="15" style="fill:rgb(44,0,0)"/>
<circle cx="125" cy="0" r="15" style="fill:rgb(0,77,0)"/>
<circle cx="187" cy="0" r="15" style="fill:rgb(0,0,232)"/>
<circle cx="250" cy="0" r="15" style="fill-opacity:0.33; fill:rgb(44,77,232)"/>
<circle cx="260" cy="0" r="15" style="fill-opacity:0.33; fill:rgb(44,77,232)"/>
<text x="250" y="62" >alpha</text>
<text x="62" y="62" >r</text>
<text x="125" y="62" >g</text>
<text x="187" y="62" >b</text>
<text x="219" y="8" >-&gt;</text>
</g>
<text x="125" y="149" style="fill:black; text-anchor:middle;font-size:24px">RGBA(r, g, b int, opacity float64)</text>
</g>
</defs>
<title>SVG Go Library Description</title>
<rect x="0" y="0" width="4500" height="3375" style="fill:white;stroke:black;stroke-width:2"/>
<g style="font-family:Calibri,sans; text-anchor:middle; font-size:24px">
<a xlink:href="http://github.com/ajstarks/svgo" xlink:title="SVGo Library">
<text x="2250" y="150" style="font-size:125px">SVG Go Library</text>
<text x="2250" y="200" style="font-size:50px;fill:gray">github.com/ajstarks/svgo</text>
</a>
<desc>Object Usage</desc>
<g transform="translate(400,400)">
<use x="0" y="0" xlink:href="#rect" />
<use x="700" y="0" xlink:href="#crect" />
<use x="1400" y="0" xlink:href="#roundrect" />
<use x="2100" y="0" xlink:href="#square" />
<use x="2800" y="0" xlink:href="#line" />
<use x="3500" y="0" xlink:href="#polyline" />
</g>
<g transform="translate(400,1000)">
<use x="0" y="0" xlink:href="#polygon" />
<use x="700" y="0" xlink:href="#circle" />
<use x="1400" y="0" xlink:href="#ellipse" />
<use x="2100" y="0" xlink:href="#arc" />
<use x="2800" y="0" xlink:href="#qbez" />
<use x="3500" y="0" xlink:href="#bezier" />
</g>
<g transform="translate(400,1600)">
<use x="0" y="0" xlink:href="#trans" />
<use x="700" y="0" xlink:href="#scale" />
<use x="1400" y="0" xlink:href="#scalexy" />
<use x="2100" y="0" xlink:href="#skewx" />
<use x="2800" y="0" xlink:href="#skewy" />
<use x="3500" y="0" xlink:href="#skewxy" />
</g>
<g transform="translate(400,2200)">
<use x="0" y="0" xlink:href="#rotate" />
<use x="700" y="0" xlink:href="#text" />
<use x="1400" y="0" xlink:href="#textpath" />
<use x="2100" y="0" xlink:href="#path" />
<use x="2800" y="0" xlink:href="#image" />
<use x="3500" y="0" xlink:href="#grid" />
</g>
<g transform="translate(400,2800)">
<use x="0" y="0" xlink:href="#lgrad" />
<use x="700" y="0" xlink:href="#rgrad" />
<use x="1400" y="0" xlink:href="#rgb" />
<use x="2100" y="0" xlink:href="#rgba" />
<use x="2800" y="0" xlink:href="#meta" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 21 KiB

3
vendor/modules.txt vendored

@ -0,0 +1,3 @@ @@ -0,0 +1,3 @@
# github.com/ajstarks/svgo v0.0.0-20200725142600-7a3c8b57fecb
## explicit
github.com/ajstarks/svgo
Loading…
Cancel
Save