diff --git a/image/README.md b/image/README.md
index 85c2ff3..9356521 100644
--- a/image/README.md
+++ b/image/README.md
@@ -40,6 +40,18 @@ mark := image.MarkSquares(yellow, chess.D2, chess.D4)
image.SVG(file, pos.Board(), mark)
```
+### Perspective
+
+Perspective is designed to be used as an optional argument
+to the SVG function. It draws the board from the perspective
+of the given color. White is the default. The following
+generates a board from Black's perspective:
+
+```go
+fromBlack := image.Perspective(chess.Black)
+image.SVG(file, pos.Board(), fromBlack)
+```
+
### Example Program
```go
diff --git a/image/black_example.svg b/image/black_example.svg
new file mode 100644
index 0000000..c6d710f
--- /dev/null
+++ b/image/black_example.svg
@@ -0,0 +1,534 @@
+
+
+
\ No newline at end of file
diff --git a/image/example.svg b/image/example.svg
index 1674249..3314905 100644
--- a/image/example.svg
+++ b/image/example.svg
@@ -4,55 +4,70 @@
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink">
-
-
-
+
+
+
+
+
+ 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;" />
+ d="M 12,35.5 L 33,35.5 L 33,35.5"
+ style="fill:none; stroke:#ffffff; stroke-width:1; stroke-linejoin:miter;" />
+ d="M 13,31.5 L 32,31.5"
+ style="fill:none; stroke:#ffffff; stroke-width:1; stroke-linejoin:miter;" />
+
+
+ style="fill:none; stroke:#ffffff; stroke-width:1; stroke-linejoin:miter;" />
-1
-a
-
-
+8
+
+
+ style="fill:#000000; stroke:#000000;" />
+ style="fill:#000000; stroke:#000000;" />
+ style="fill:#ffffff; stroke:#ffffff;" />
+ style="fill:#ffffff; stroke:#ffffff;" />
+
-b
-
-
+
+
-
+
+ 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;" />
-c
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+ 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;" />
+ 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;" />
+ d="M 11,38.5 A 35,35 1 0 0 34,38.5"
+ style="fill:none; stroke:#000000; stroke-linecap:butt;" />
+ d="M 11,29 A 35,35 1 0 1 34,29"
+ style="fill:none; stroke:#ffffff;" />
+ d="M 12.5,31.5 L 32.5,31.5"
+ style="fill:none; stroke:#ffffff;" />
+ d="M 11.5,34.5 A 35,35 1 0 0 33.5,34.5"
+ style="fill:none; stroke:#ffffff;" />
+ d="M 10.5,37.5 A 35,35 1 0 0 34.5,37.5"
+ style="fill:none; stroke:#ffffff;" />
-d
-
-
+
+
-
+ d="M 22.5,11.63 L 22.5,6"
+ style="fill:none; stroke:#000000; stroke-linejoin:miter;"
+ id="path6570" />
+ 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;" />
+ 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;" />
+ d="M 20,8 L 25,8"
+ style="fill:none; stroke:#000000; stroke-linejoin:miter;" />
+ 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;" />
+ 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;" />
-e
-
-
+
+
-
+
+ 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;" />
-f
-
-
+
+
+ style="fill:#000000; stroke:#000000;" />
+ style="fill:#000000; stroke:#000000;" />
+ style="fill:#ffffff; stroke:#ffffff;" />
+ style="fill:#ffffff; stroke:#ffffff;" />
+
-g
-
-
-
+
+
+
+
+
+ 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;" />
+ d="M 12,35.5 L 33,35.5 L 33,35.5"
+ style="fill:none; stroke:#ffffff; stroke-width:1; stroke-linejoin:miter;" />
+ d="M 13,31.5 L 32,31.5"
+ style="fill:none; stroke:#ffffff; stroke-width:1; stroke-linejoin:miter;" />
+
+
+ style="fill:none; stroke:#ffffff; stroke-width:1; stroke-linejoin:miter;" />
-h
-
-
+
+
+ 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;" />
-2
-
-
+7
+
+
+ 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;" />
-
-
+
+
+ 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;" />
-
-
-
-
+
+
+ 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;" />
-
-
+
+
+ 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;" />
-
-
+
+
+ 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;" />
-
-
+
+
+ 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;" />
-
-3
-
-
-
-
-
-
-
-
-4
-
-
-
-
-
+
+
+ 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;" />
-
-
-
-
-
-5
-
-
-
-
-
-
-
6
@@ -274,119 +270,133 @@
-
-
+
+5
+
+
+
+
+
+
+
+
+4
+
+
+
+
+
+ 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;" />
-7
-
-
+
+
+
+
+
+3
+
+
+
+
+
+
+
+
+
+ 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;" />
-
-
+2
+
+
+ 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;" />
-
-
+
+
+ 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;" />
-
-
+
+
+
+
+ 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;" />
-
-
+
+
+ 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;" />
-
-
+
+
+ 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;" />
-
-
+
+
+ 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;" />
-
-
-
+
+
+
-
-
-
-
+ d="M 34,14 L 31,17 L 14,17 L 11,14" />
+ d="M 31,17 L 31,29.5 L 14,29.5 L 14,17"
+ style="stroke-linecap:butt; stroke-linejoin:miter;" />
+ d="M 31,29.5 L 32.5,32 L 12.5,32 L 14,29.5" />
+ style="fill:none; stroke:#000000; stroke-linejoin:miter;" />
-8
-
-
+1
+a
+
+
+ style="fill:#ffffff; stroke:#000000;" />
+ style="fill:#ffffff; stroke:#000000;" />
+ style="fill:#000000; stroke:#000000;" />
-
+ style="fill:#000000; stroke:#000000;" />
-
-
+b
+
+
-
+
+ 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;" />
-
-
-
-
-
-
-
-
-
-
+c
+
+
+
+ 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)" />
+ 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)" />
+ 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)" />
+ 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)" />
+ 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)" />
+ 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;" />
+ 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;" />
+
+
-
-
+d
+
+
+ d="M 22.5,11.63 L 22.5,6"
+ style="fill:none; stroke:#000000; stroke-linejoin:miter;" />
+ d="M 20,8 L 25,8"
+ style="fill:none; stroke:#000000; stroke-linejoin:miter;" />
+ 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;" />
+ 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;" />
+ d="M 11.5,30 C 17,27 27,27 32.5,30"
+ style="fill:none; stroke:#000000;" />
+ d="M 11.5,33.5 C 17,30.5 27,30.5 32.5,33.5"
+ style="fill:none; stroke:#000000;" />
+
-
-
+e
+
+
-
+
+ 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;" />
-
-
+f
+
+
+ style="fill:#ffffff; stroke:#000000;" />
+ style="fill:#ffffff; stroke:#000000;" />
+ style="fill:#000000; stroke:#000000;" />
-
+ style="fill:#000000; stroke:#000000;" />
-
-
-
+g
+
+
+
-
-
-
-
+ d="M 34,14 L 31,17 L 14,17 L 11,14" />
+ d="M 31,17 L 31,29.5 L 14,29.5 L 14,17"
+ style="stroke-linecap:butt; stroke-linejoin:miter;" />
+ d="M 31,29.5 L 32.5,32 L 12.5,32 L 14,29.5" />
+ style="fill:none; stroke:#000000; stroke-linejoin:miter;" />
+h
\ No newline at end of file
diff --git a/image/image.go b/image/image.go
index f23217b..9599390 100755
--- a/image/image.go
+++ b/image/image.go
@@ -42,12 +42,22 @@ func MarkSquares(c color.Color, sqs ...chess.Square) func(*encoder) {
}
}
+// Perspective is designed to be used as an optional argument
+// to the SVG function. It draws the board from the perspective
+// of the given color. White is the default.
+func Perspective(c chess.Color) func(*encoder) {
+ return func(e *encoder) {
+ e.perspective = 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
+ w io.Writer
+ light color.Color
+ dark color.Color
+ perspective chess.Color
+ marks map[chess.Square]color.Color
}
// New returns an encoder that writes to the given writer.
@@ -55,10 +65,11 @@ type encoder struct {
// 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{},
+ w: w,
+ light: color.RGBA{235, 209, 166, 1},
+ dark: color.RGBA{165, 117, 81, 1},
+ perspective: chess.White,
+ marks: map[chess.Square]color.Color{},
}
for _, op := range options {
op(e)
@@ -74,8 +85,10 @@ const (
)
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}
+ orderOfRanks = []chess.Rank{chess.Rank8, chess.Rank7, chess.Rank6, chess.Rank5, chess.Rank4, chess.Rank3, chess.Rank2, chess.Rank1}
+ orderOfRanksBlack = []chess.Rank{chess.Rank1, chess.Rank2, chess.Rank3, chess.Rank4, chess.Rank5, chess.Rank6, chess.Rank7, chess.Rank8}
+ orderOfFiles = []chess.File{chess.FileA, chess.FileB, chess.FileC, chess.FileD, chess.FileE, chess.FileF, chess.FileG, chess.FileH}
+ orderOfFilesBlack = []chess.File{chess.FileH, chess.FileG, chess.FileF, chess.FileE, chess.FileD, chess.FileC, chess.FileB, chess.FileA}
)
// EncodeSVG writes the board SVG representation into
@@ -87,34 +100,42 @@ func (e *encoder) EncodeSVG(b *chess.Board) error {
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
+ ranks := orderOfRanks
+ files := orderOfFiles
+ if e.perspective == chess.Black {
+ ranks = orderOfRanksBlack
+ files = orderOfFilesBlack
+ }
+ for i, rank := range ranks {
+ for j, file := range files {
+ x := j * sqHeight
+ y := i * sqHeight
+ sq := chess.NewSquare(file, rank)
+ 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 j == 0 {
+ 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 i == 7 {
+ 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)
}
- }
- // 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()
@@ -137,12 +158,6 @@ func (e *encoder) colorForText(sq chess.Square) color.Color {
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))
diff --git a/image/image_test.go b/image/image_test.go
index f0bb21f..b442c52 100755
--- a/image/image_test.go
+++ b/image/image_test.go
@@ -14,7 +14,8 @@ import (
"github.com/notnil/chess/image"
)
-const expectedMD5 = "da140af8b83ce7903915ee39973e36dd"
+const expectedMD5 = "08aaa6fcfde3bb900fc54bdfef3d5c81"
+const expectedMD5Black = "badac5ca5cfbdea9b98a1f9988ba54bc"
func TestSVG(t *testing.T) {
// create buffer of actual svg
@@ -46,3 +47,35 @@ func TestSVG(t *testing.T) {
t.Error(err)
}
}
+
+func TestSVGFromBlack(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)
+ per := image.Perspective(chess.Black)
+ if err := image.SVG(buf, pos.Board(), mark, per); err != nil {
+ t.Error(err)
+ }
+
+ // compare to expected svg
+ actualSVG := strings.TrimSpace(buf.String())
+ actualMD5 := fmt.Sprintf("%x", md5.Sum([]byte(actualSVG)))
+ if actualMD5 != expectedMD5Black {
+ t.Errorf("expected actual md5 hash to be %s but got %s", expectedMD5Black, actualMD5)
+ }
+
+ // create actual svg file for visualization
+ f, err := os.Create("black_example.svg")
+ defer f.Close()
+ if err != nil {
+ t.Error(err)
+ }
+ if _, err := io.Copy(f, bytes.NewBufferString(actualSVG)); err != nil {
+ t.Error(err)
+ }
+}