Skip to content

Instantly share code, notes, and snippets.

@anastasop
Created July 7, 2012 15:32
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save anastasop/3066893 to your computer and use it in GitHub Desktop.
Save anastasop/3066893 to your computer and use it in GitHub Desktop.
Describe a chess position in FEN and get the image
/*
A simple Go program, http://golang.org, i wrote to demonstrate the
language. It converts chess FEN notation to images or html tables.
Uses the public domain pieces from http://www.crockford.com/chess/pieces.html
The program is small in size but not in functionality. It includes
http client and server, html templates, image processing, strings, io
and demonstrates the expressiveness of Go
See http://blog.golang.org/2011/12/from-zero-to-go-launching-on-google.html
for a more advanced example of the same APIs running on GAE
Usage:
- go run fen.go
- open chrome to http://localhost:9000 to see the examples
by http://www.twitter.com/anastasop
*/
package main
import (
"bytes"
"fmt"
"strings"
"io"
"io/ioutil"
"image"
"image/gif"
"image/png"
"image/draw"
"html/template"
"net/http"
)
var piecesImages map[string]image.Image = make(map[string]image.Image)
var piecesGIFs map[string][]byte = make(map[string][]byte)
var boardTemplate *template.Template
func downloadPiece(url string) (image.Image, []byte, error) {
r, e := http.Get(url)
if e != nil {
return nil, nil, fmt.Errorf("failed to download piece %s: %s", url, e)
}
defer r.Body.Close()
pix, e := ioutil.ReadAll(r.Body)
if e != nil {
return nil, nil, fmt.Errorf("failed to download piece %s: %s", url, e)
}
img, e := gif.Decode(bytes.NewReader(pix))
if e != nil {
return nil, nil, fmt.Errorf("failed to decode piece %s: %s", url, e)
}
return img, pix, nil
}
/* download the public domain chess pieces from http://www.crockford.com/chess/pieces.html */
func downloadPieces() {
baseurl := "http://www.crockford.com/chess/"
for _, square := range "wb" {
for _, color := range "wb" {
for _, name := range "prnbqk" {
key := string([]rune{color, name, square})
if img, pix, e := downloadPiece(baseurl + key + ".gif"); e != nil {
panic(e)
} else {
piecesImages[key] = img
piecesGIFs[key] = pix
}
}
}
}
if img, pix, e := downloadPiece(baseurl + "w" + ".gif"); e != nil {
panic(e)
} else {
piecesImages["w"] = img
piecesGIFs["w"] = pix
}
if img, pix, e := downloadPiece(baseurl + "b" + ".gif"); e != nil {
panic(e)
} else {
piecesImages["b"] = img
piecesGIFs["b"] = pix
}
}
func pieceHandler(w http.ResponseWriter, req *http.Request) {
parts := strings.Split(req.URL.Path, "/")
key := parts[len(parts) - 1]
if v, ok := piecesGIFs[key]; ok {
w.Header().Set("Content-Type", "image/gif")
w.Write(v)
} else {
http.NotFound(w, req)
}
}
func fenHandler(w http.ResponseWriter, req *http.Request) {
fen := req.FormValue("fen")
if fen == "" {
fen = "rnbqkbnr/pppppppp/////PPPPPPPP/RNBQKBNR"
}
how := req.FormValue("r")
if how == "" {
how = "html"
}
board := parse(fen)
switch how {
default:
w.Header().Set("Content-Type", "text/html")
boardTemplate.Execute(w, board)
case "image":
w.Header().Set("Content-Type", "image/png")
w.Header().Set("Cache-Control", "no-store, no-cache")
img := image.NewRGBA(image.Rect(0, 0, 256, 256))
for r, rank := range board {
for f, piece := range rank {
p := image.Pt(f * 32, r * 32)
draw.Draw(img, image.Rect(p.X, p.Y, p.X + 32, p.Y + 32),
piecesImages[piece], image.ZP, draw.Src)
}
}
png.Encode(w, img)
}
}
func main() {
downloadPieces()
t, e := template.New("Board").Parse(
`<table>{{range .}}<tr>{{range .}}<td><img src="../pieces/{{.}}"/></td>{{end}}</tr>{{end}}</table>`)
if e != nil {
panic(e)
} else {
boardTemplate = t
}
http.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) {
io.WriteString(w, indexPage)
});
http.HandleFunc("/board", fenHandler)
http.HandleFunc("/pieces/", pieceHandler)
e = http.ListenAndServe(":9000", nil)
if e != nil {
panic(e)
}
}
func parse(fen string) [8][8]string {
var board [8][8]string
fen = strings.TrimSpace(fen)
ranks := strings.Split(fen +"/////////", "/")[:8]
repl := strings.NewReplacer(
"1", "*", "2", "**", "3", "***", "4", "****", "5", "*****",
"6", "******", "7", "*******", "8", "********")
var colors = [2]string{"w", "b"}
for r, rank := range ranks {
s := (repl.Replace(rank) + "********")[:8]
for f, p := range s {
var piece string
switch p {
case 'P', 'R', 'N', 'B', 'Q', 'K':
piece = "w" + strings.ToLower(string(p))
case 'p', 'r', 'n', 'b', 'q', 'k':
piece = "b" + strings.ToLower(string(p))
default:
piece = ""
}
board[r][f] = piece + colors[(f + (r % 2)) % 2]
}
}
return board
}
var indexPage string = `<!DOCTYPE html>
<html>
<head>
<title>Chess Board Generator</title>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
<script>
$(function() {
$("#asTable").click(function(e) {
var fen = $("#fen").val()
$("#board").load('/board', "fen=" + encodeURIComponent(fen) + "&r=html");
});
$("#asImage").click(function(e) {
var fen = $("#fen").val()
var url = "/board?fen=" + encodeURIComponent(fen) + "&r=image";
$("#board").html('<img src="' + url + '">');
});
$("#asImage").click()
});
</script>
</head>
<body>
<h1>Chess Board Generator</h1>
Enter a chess board notation in <a href="http://en.wikipedia.org/wiki/Forsyth%E2%80%93Edwards_Notation">FEN</a> and see it either as an html table of individual images or as a single PNG image
<h2>Examples</h2>
<ul class="examples">
<li>Starting Position - <b>rnbqkbnr/pppppppp/////PPPPPPPP/RNBQKBNR</b></li>
<li>Lucena Position - <b>1K1k/1P6///r///2R</b></li>
<li>Kasparov vs Topalov 1999 - <b>b2r3r/k4p1p/p2q1np1/NppP4/3p1Q2/P4PPB/1PP4P/1K1RR3</b></li>
</ul>
<label for="fen">FEN:</label><input type="text" size="66" id="fen" value="4k///////4K" />
<button id="asImage">Image</button><button id="asTable">Table</button><br><br>
<div id="board"></div>
</body>
</html>
`
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment