Created
July 7, 2012 15:32
-
-
Save anastasop/3066893 to your computer and use it in GitHub Desktop.
Describe a chess position in FEN and get the image
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
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