Een schaakbord is een vierkant spelbord dat gebruikt wordt om te schaken. Het bord is opgedeeld in 64 vierkante velden waarop schaakstukken geplaatst worden. De velden staan op het bord gerangschikt in een $$8 \times 8$$ rooster met 8 rijen en 8 kolommen, en worden afwisselend licht en donker gekleurd. Bij een correcte plaatsing van het bord heeft elke speler een licht gekleurd veld in zijn rechter onderhoek.

schaakbord
Een schaakbord is een vierkant spelbord dat gebruikt wordt om te schaken. Het bord is opgedeeld in 64 vierkante velden waarop schaakstukken geplaatst worden. De velden staan op het bord gerangschikt in een $$8 \times 8$$ rooster met 8 rijen en 8 kolommen, en worden afwisselend licht en donker gekleurd. Bij een correcte plaatsing van het bord heeft elke speler een licht gekleurd veld in zijn rechter onderhoek. De opstelling van de schaakstukken op dit schaakbord kan voorgesteld worden met de FEN-string 4r3/2P3R1/R1N2k1P/5Np1/K1p1p3/1pr5/3P4/Bn3Q2.

De kolommen van een schaakbord worden files genoemd en de rijen worden ranks genoemd. De FIDE-standaard1 gebruikt de algebraïsche notatie om de velden op het bord aan te duiden. Daarbij worden — vanuit het perspectief van wit — de files van links naar rechts gelabeld met de letters a tot en met h, en de ranks van onder naar boven met de cijfers 1 tot en met 8. Daardoor kan elk veld aangeduid worden door de file en de rank die het inneemt in het rooster. De files a tot en met d vormen de koninginnenkant en de files e tot en met h vormen de koningskant. De ranks 1 tot en met 4 vormen de kant van wit en de rijen 5 tot en met 8 vormen de kant van zwart.

FEN

De Forsyth-Edwards Notation (FEN) is een standaardnotatie om een bepaalde bordpositie van een schaakpartij te beschrijven. Het doel is dat FEN alle informatie bevat die nodig is om een partij opnieuw te beginnen vanuit een bepaalde opstelling. FEN is gebaseerd op een systeem dat ontwikkeld werd door de Schotse journalist David Forsyth2. Zijn systeem werd populair in de 19e eeuw. Steven J. Edwards3 breidde het later uit voor gebruik door computers.

Een FEN-record definieert een bepaalde spelpositie van een schaakspel op één tekstregel die enkel uit ASCII-karakters bestaat. Een FEN-record bevat zes velden, die telkens van elkaar gescheiden worden door een spatie. Wij gebruiken enkel het eerste veld dat de plaatsing van de schaakstukken op het schaakbord beschrijft. Dit is bijvoorbeeld de FEN-string voor de bordpositie van het schaakbord in de afbeelding hierboven:

4r3/2P3R1/R1N2k1P/5Np1/K1p1p3/1pr5/3P4/Bn3Q2

De FEN-string bestaat uit een beschrijving van elke rank (rij), beginnend met rank 8 en eindigend met rank 1. De beschrijvingen van de ranks worden van elkaar gescheiden met een slash (/). De schaakstukken op elke rank worden beschreven vanaf file a tot en met file h. Daarbij wordt elk schaakstuk aangeduid met één letter uit de Engelse standaardnamen in algebraïsche notatie: K (koning), Q (koningin), R (toren), B (loper), N (paard) en P (pion). De witte schaakstukken worden aangeduid met hoofdletters (KQRBNP) en de zwarte schaakstukken met kleine letters (kqrbnp). Een reeks van één of meer opeenvolgende lege velden binnen een rank wordt aangeduid met een cijfer van 1 tot en met 8, dat overeenkomt met het aantal opeenvolgende lege velden. Op die manier worden de acht files van elke rank beschreven: een letter beschrijft één file en een cijfer beschrijft het corresponderend aantal files. Er staan ook nooit twee cijfers na elkaar.

Opgave

Schrijf een bash shell script fen dat voor een gegeven FEN-string een grafische voorstelling van de schaakbordopstelling uitschrijft naar standaard uitvoer (stdout) in SVG-formaat. De FEN-string die de opstelling van de schaakstukken op het schaakbord beschrijft, moet als argument aan het script doorgegeven worden. Verderop4 bespreken we stap voor stap hoe de SVG-afbeelding opgebouwd wordt. Het script moet bijvoorbeeld kunnen gebruikt worden om de afbeelding uit de inleiding van deze opgave te genereren met deze FEN-string:

4r3/2P3R1/R1N2k1P/5Np1/K1p1p3/1pr5/3P4/Bn3Q2

Het shell script schrijft dan de volgende SVG-afbeelding uit naar stdout (chess.svg5):

<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="400" height="400" viewBox="-1 -1 9 9">
<style>
text{font-family:consolas;font-size:0.2px;}
text.piece{text-anchor:middle;dominant-baseline:mathematical;font-size:0.8px;}
text.horizontal{text-anchor:middle;dominant-baseline:middle;fill:grey;}
text.vertical{writing-mode:tb;glyph-orientation-vertical:0;fill:grey;}
text.vertical{writing-mode:vertical-rl;text-orientation:upright;}
</style>
<rect x="-1" y="-1" width="9" height="9" fill="black" rx="0.2"/>
<rect x="-0.5" y="-0.5" width="8" height="8" fill="#d28c45"/>
<path d="M-.5,0h7M.5,1h7M-.5,2h7M.5,3h7" stroke="#ffce9e" stroke-dasharray="1"/>
<path d="M-.5,4h7M.5,5h7M-.5,6h7M.5,7h7" stroke="#ffce9e" stroke-dasharray="1"/>
<text class="horizontal" x="3.5" y="-.75" textLength="7.1">abcdefgh</text>
<text class="horizontal" x="3.5" y="7.75" textLength="7.1">abcdefgh</text>
<text class="vertical" x="-.75" y="-.2" textLength="7.3">87654321</text>
<text class="vertical" x="7.75" y="-.2" textLength="7.3">87654321</text>
<text class="piece" x="4" y="0">&#9820;</text>
<text class="piece" x="2" y="1">&#9817;</text>
<text class="piece" x="6" y="1">&#9814;</text>
<text class="piece" x="0" y="2">&#9814;</text>
<text class="piece" x="2" y="2">&#9816;</text>
<text class="piece" x="5" y="2">&#9818;</text>
<text class="piece" x="7" y="2">&#9817;</text>
<text class="piece" x="5" y="3">&#9816;</text>
<text class="piece" x="6" y="3">&#9823;</text>
<text class="piece" x="0" y="4">&#9812;</text>
<text class="piece" x="2" y="4">&#9823;</text>
<text class="piece" x="4" y="4">&#9823;</text>
<text class="piece" x="1" y="5">&#9823;</text>
<text class="piece" x="2" y="5">&#9820;</text>
<text class="piece" x="3" y="6">&#9817;</text>
<text class="piece" x="0" y="7">&#9815;</text>
<text class="piece" x="1" y="7">&#9822;</text>
<text class="piece" x="5" y="7">&#9813;</text>
</svg>

Het script moet de volgende opties ondersteunen:

Het script moet voor de verwerking van de opties de flexibiliteit aan de dag leggen die gebruikelijk is bij Unix commando's: volgorde van opties speelt geen rol, opties kunnen samengenomen worden, argument bij een optie moet niet noodzakelijk van de optieletter gescheiden worden door witruimte, …. Daarnaast moet het script de volgende foutafhandeling voorzien:

De gepaste foutboodschappen vind je terug in onderstaand voorbeeld.

Opbouw van de SVG-afbeelding

Scalable Vector Graphics (SVG8) is een op XML9 gebaseerd bestandsformaat dat vectorafbeeldingen10 tekstueel beschrijft aan de hand van eenvoudige meetkundige bouwstenen zoals punten, lijnen, cirkels en veelhoeken.

Je moet geen SVG of XML kennen om deze opgave op te lossen. We bespreken stap voor stap hoe de grafische voorstelling van de schaakbordopstelling in SVG-formaat opgebouwd wordt aan de hand van vier sjablonen: schaakbord11, labels12, schaakstuk13 en voettekst14. Daarbij zullen we de variabele onderdelen van elk sjabloon in het groen weergeven. Daar moet het script de juiste waarden invullen. Het is belangrijk dat elk sjabloon exact overgenomen wordt zodat het resultaat dat het script uitschrijft precies overeenkomt met de beschreven specificatie.

Schaakbord
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="400" height="400" viewBox="-1 -1 9 9">
<style>
text{font-family:consolas;font-size:0.2px;}
text.piece{text-anchor:middle;dominant-baseline:mathematical;font-size:0.8px;}
text.horizontal{text-anchor:middle;dominant-baseline:middle;fill:grey;}
text.vertical{writing-mode:tb;glyph-orientation-vertical:0;fill:grey;}
text.vertical{writing-mode:vertical-rl;text-orientation:upright;}
</style>
<rect x="-1" y="-1" width="9" height="9" fill="black" rx="0.2"/>
<rect x="-0.5" y="-0.5" width="8" height="8" fill="#d28c45"/>
<path d="M-.5,0h7M.5,1h7M-.5,2h7M.5,3h7" stroke="#ffce9e" stroke-dasharray="1"/>
<path d="M-.5,4h7M.5,5h7M-.5,6h7M.5,7h7" stroke="#ffce9e" stroke-dasharray="1"/>

Dit sjabloon bevat de SVG start-tag (svg), algemene opmaak van de afbeelding (style) en tekent het schaakbord met een rand (eerste rect) en lichte en donkere velden.

De drie opmaakregels die beginnen met text.horizontal en text.vertical (weergegeven in het grijs in het sjabloon) mogen niet uitgeschreven worden als de labels rondom het schaakbord niet moeten weergegeven worden.

De opvulkleur (fill) van de tweede rechthoek (rect) moet ingesteld worden met de kleur voor de donkere velden. De lijnkleur (stroke) van de twee paden (path) moet ingesteld worden met de kleur voor de lichte velden.

Labels
<text class="horizontal" x="3.5" y="-.75" textLength="7.1">abcdefgh</text>
<text class="horizontal" x="3.5" y="7.75" textLength="7.1">abcdefgh</text>
<text class="vertical" x="-.75" y="-.2" textLength="7.3">87654321</text>
<text class="vertical" x="7.75" y="-.2" textLength="7.3">87654321</text>

Dit tekent de labels rondom de velden van het schaakbord. Dit sjabloon mag enkel uitgeschreven worden als de labels rondom het schaakbord moeten weergegeven worden.

Schaakstuk

Om de schaakstukken op het schaakbord te tekenen, gebruikt de SVG-afbeelding een coördinatensysteem waarbij de positie van een velden voorgesteld wordt met een $$(x, y)$$-coördinaat met natuurlijke getallen als coördinaten. Daarbij worden de rijen (ranks) van boven naar onder genummerd, en de kolommen (files) van links naar rechts, telkens vanaf nul. De $$x$$-coördinaat geeft dan het rijnummer van het veld aan, en de $$y$$-coördinaat het kolomnummer.

coördinaten
Om de schaakstukken op het schaakbord te tekenen, gebruikt de SVG-afbeelding een coördinatensysteem waarbij de positie van een velden voorgesteld wordt met een $$(x, y)$$-coördinaat met natuurlijke getallen als coördinaten. Daarbij worden de rijen (ranks) van boven naar onder genummerd, en de kolommen (files) van links naar rechts, telkens vanaf nul. De $$x$$-coördinaat geeft dan het rijnummer van het veld aan, en de $$y$$-coördinaat het kolomnummer.

Voor elk schaakstuk op het spelbord moet een regel uitgeschreven worden volgens dit sjabloon.

<text class="piece" x="4" y="0">&#9820;</text>

Dit tekent een schaakstuk in het veld met gegeven $$x$$-coördinaat (x) en $$y$$-coördinaat (y).

Het schaakstuk zelf wordt weergegeven als de decimale voorstelling van een Unicode-karakter (&#dddd;). Daarbij corresponderen de hoofdletters KQRBNP uit de FEN-string met de decimale waarden 98129817, en de kleine letters kqrbnp met de decimale waarden 98189823.

De schaakstukken moeten in leesvolgorde uitgeschreven worden: daarbij worden de velden van links naar rechts, en van boven naar onder doorlopen. Dit is dezelfde volgorde als de beschrijving van de schaakstukken in de FEN-string.

Voettekst
</svg>

Dit laatste sjabloon sluit de SVG-afbeelding af met een SVG stop-tag.

Voorbeeld

Onderstaande voorbeeldsessie toont hoe het shell script fen moet kunnen gebruikt worden.

$ fen -n 4r3/2P3R1/R1N2k1P/5Np1/K1p1p3/1pr5/3P4/Bn3Q2 > chess.01.svg15
$ cat chess.01.svg16
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="400" height="400" viewBox="-1 -1 9 9">
<style>
text{font-family:consolas;font-size:0.2px;}
text.piece{text-anchor:middle;dominant-baseline:mathematical;font-size:0.8px;}
text.horizontal{text-anchor:middle;dominant-baseline:middle;fill:grey;}
text.vertical{writing-mode:tb;glyph-orientation-vertical:0;fill:grey;}
text.vertical{writing-mode:vertical-rl;text-orientation:upright;}
</style>
<rect x="-1" y="-1" width="9" height="9" fill="black" rx="0.2"/>
<rect x="-0.5" y="-0.5" width="8" height="8" fill="#d28c45"/>
<path d="M-.5,0h7M.5,1h7M-.5,2h7M.5,3h7" stroke="#ffce9e" stroke-dasharray="1"/>
<path d="M-.5,4h7M.5,5h7M-.5,6h7M.5,7h7" stroke="#ffce9e" stroke-dasharray="1"/>
<text class="horizontal" x="3.5" y="-.75" textLength="7.1">abcdefgh</text>
<text class="horizontal" x="3.5" y="7.75" textLength="7.1">abcdefgh</text>
<text class="vertical" x="-.75" y="-.2" textLength="7.3">87654321</text>
<text class="vertical" x="7.75" y="-.2" textLength="7.3">87654321</text>
<text class="piece" x="4" y="0">&#9820;</text>
<text class="piece" x="2" y="1">&#9817;</text>
<text class="piece" x="6" y="1">&#9814;</text>
<text class="piece" x="0" y="2">&#9814;</text>
<text class="piece" x="2" y="2">&#9816;</text>
<text class="piece" x="5" y="2">&#9818;</text>
<text class="piece" x="7" y="2">&#9817;</text>
<text class="piece" x="5" y="3">&#9816;</text>
<text class="piece" x="6" y="3">&#9823;</text>
<text class="piece" x="0" y="4">&#9812;</text>
<text class="piece" x="2" y="4">&#9823;</text>
<text class="piece" x="4" y="4">&#9823;</text>
<text class="piece" x="1" y="5">&#9823;</text>
<text class="piece" x="2" y="5">&#9820;</text>
<text class="piece" x="3" y="6">&#9817;</text>
<text class="piece" x="0" y="7">&#9815;</text>
<text class="piece" x="1" y="7">&#9822;</text>
<text class="piece" x="5" y="7">&#9813;</text>
</svg>
$ fen -n -l "#c9b458" -d "#6aaa64" rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR > chess.02.svg17

$ fen -x 4r3/2P3R1/R1N2k1P/5Np1/K1p1p3/1pr5/3P4/Bn3Q2
Usage: fen [-n] [-l <color>] [-d <color>] <FEN-string>
$ echo $?
1
$ fen -n
Usage: fen [-n] [-l <color>] [-d <color>] <FEN-string>
$ echo $?
1
$ fen -n 4r3/2P3R1
fen: invalid FEN-string
$ echo $?
2
$ fen -n 2r1/8/8/8/8/8/8/8
fen: invalid FEN-string
$ echo $?
2
$ fen -n 4X3/8/8/8/8/8/8/8
fen: invalid FEN-string
$ echo $?
2

Epiloog

De Forsyth-Edwards Notation (FEN) heeft geen ingebouwde controlemechanismen om na te gaan of de beschreven schaakbordopstelling volgens de FIDE18-regels wel degelijk kan volgen uit de beginopstelling. Dat is immers niet voor alle opstellingen het geval. FEN laat bijvoorbeeld toe dat er pionnen op de bovenste of onderste rij staan, maar dergelijke opstellingen zullen gewoonlijk door schaakprogramma's geweigerd worden. Een voordeel van de ontbrekende integriteitscontrole is dat sommige schaakvarianten en puzzels — zoals schaak 96019 en het achtkoninginnenvraagstuk20 — met FEN kunnen beschreven worden.