← All the games

Chess

All of Chess is here, in Quantum's game language: chess.q, compiled for Godot 4.4.1, with no script besides. Point at a tag or an attribute to see what it does; a tag opens its entry in the language's reference.

Play in the browser
The 22 tags it uses, of the language's 145 qg:button qg:cursor qg:destroy qg:fullscreen qg:goto-scene qg:hud qg:instance qg:language qg:lobby qg:menu qg:multiplayer qg:on-select qg:phrase qg:prefab qg:put qg:scene qg:spawn qg:spritesheet qg:swap qg:text qg:tilemap qg:tileset
chess.q371 lines Download On GitHub
<q:application id="chess" type="game">  <!-- Chess: the rules of github.com/Prashanna135/chess (MIT) — legal moves,       castling, en passant, promotion, check, checkmate and stalemate —       written in the game language's own statements and expressions over an       array of 64 squares; the pieces on the screen mirror it. White is       player 1, black player 2: on one machine both click the same board;       over the network each machine is one colour, in lockstep. README.md       maps the original to this file. -->  <qg:tileset name="board" src="assets/board.png" tile="62" />  <qg:spritesheet name="pieces" src="assets/pieces.png" tile="60" />  <qg:multiplayer players="2" delay="2" start="game" />  <qg:prefab name="wK" tag="piece" sheet="pieces" frame="0" hitbox="60x60" />  <qg:prefab name="wQ" tag="piece" sheet="pieces" frame="1" hitbox="60x60" />  <qg:prefab name="wR" tag="piece" sheet="pieces" frame="2" hitbox="60x60" />  <qg:prefab name="wB" tag="piece" sheet="pieces" frame="3" hitbox="60x60" />  <qg:prefab name="wN" tag="piece" sheet="pieces" frame="4" hitbox="60x60" />  <qg:prefab name="wP" tag="piece" sheet="pieces" frame="5" hitbox="60x60" />  <qg:prefab name="bK" tag="piece" sheet="pieces" frame="6" hitbox="60x60" />  <qg:prefab name="bQ" tag="piece" sheet="pieces" frame="7" hitbox="60x60" />  <qg:prefab name="bR" tag="piece" sheet="pieces" frame="8" hitbox="60x60" />  <qg:prefab name="bB" tag="piece" sheet="pieces" frame="9" hitbox="60x60" />  <qg:prefab name="bN" tag="piece" sheet="pieces" frame="10" hitbox="60x60" />  <qg:prefab name="bP" tag="piece" sheet="pieces" frame="11" hitbox="60x60" />  <!-- What the board shows besides the pieces (assets/board.png): the picked square, a dot on each       square the picked piece may go to (a ring round a piece it may take), the last move, a king in check. -->  <qg:prefab name="Marker" tag="marker" sheet="board" frame="2" hitbox="4x4" />  <qg:prefab name="Dot" tag="dot" sheet="board" frame="3" hitbox="4x4" gd:z_index="1" />  <qg:prefab name="Ring" tag="dot" sheet="board" frame="4" hitbox="4x4" gd:z_index="1" />  <qg:prefab name="Last" tag="last" sheet="board" frame="6" hitbox="4x4" />  <qg:prefab name="Check" tag="check" sheet="board" frame="7" hitbox="4x4" />  <!-- the game in Portuguese and Spanish (a web page's ?lang=pt, the command line's q-lang, or the system's language) -->  <qg:language lang="pt" name="Português">    <qg:phrase text="CHESS" as="XADREZ" />    <qg:phrase text="Two players, one board" as="Dois jogadores, um tabuleiro" />    <qg:phrase text="White to move - click a piece, then where it goes" as="Brancas jogam - clique numa peça, depois aonde ela vai" />    <qg:phrase text="White to move" as="Brancas jogam" />    <qg:phrase text="Black to move" as="Pretas jogam" />    <qg:phrase text="White to move - check" as="Brancas jogam - xeque" />    <qg:phrase text="Black to move - check" as="Pretas jogam - xeque" />    <qg:phrase text="White to move - that piece cannot move" as="Brancas jogam - essa peça não pode se mover" />    <qg:phrase text="Black to move - that piece cannot move" as="Pretas jogam - essa peça não pode se mover" />    <qg:phrase text="White to move - that piece cannot move - check" as="Brancas jogam - essa peça não pode se mover - xeque" />    <qg:phrase text="Black to move - that piece cannot move - check" as="Pretas jogam - essa peça não pode se mover - xeque" />    <qg:phrase text="Checkmate - White wins" as="Xeque-mate - as brancas vencem" />    <qg:phrase text="Checkmate - Black wins" as="Xeque-mate - as pretas vencem" />    <qg:phrase text="Stalemate" as="Afogamento - empate" />      <qg:phrase text="OPTIONS" as="OPÇÕES" />    <qg:phrase text="Full screen" as="Tela cheia" />    <qg:phrase text="on" as="sim" />    <qg:phrase text="off" as="não" />    <qg:phrase text="Back" as="Voltar" /></qg:language>  <qg:language lang="es" name="Español">    <qg:phrase text="CHESS" as="AJEDREZ" />    <qg:phrase text="Two players, one board" as="Dos jugadores, un tablero" />    <qg:phrase text="White to move - click a piece, then where it goes" as="Juegan blancas - haz clic en una pieza y luego adónde va" />    <qg:phrase text="White to move" as="Juegan blancas" />    <qg:phrase text="Black to move" as="Juegan negras" />    <qg:phrase text="White to move - check" as="Juegan blancas - jaque" />    <qg:phrase text="Black to move - check" as="Juegan negras - jaque" />    <qg:phrase text="White to move - that piece cannot move" as="Juegan blancas - esa pieza no puede moverse" />    <qg:phrase text="Black to move - that piece cannot move" as="Juegan negras - esa pieza no puede moverse" />    <qg:phrase text="White to move - that piece cannot move - check" as="Juegan blancas - esa pieza no puede moverse - jaque" />    <qg:phrase text="Black to move - that piece cannot move - check" as="Juegan negras - esa pieza no puede moverse - jaque" />    <qg:phrase text="Checkmate - White wins" as="Jaque mate - ganan las blancas" />    <qg:phrase text="Checkmate - Black wins" as="Jaque mate - ganan las negras" />    <qg:phrase text="Stalemate" as="Rey ahogado - tablas" />      <qg:phrase text="OPTIONS" as="OPCIONES" />    <qg:phrase text="Full screen" as="Pantalla completa" />    <qg:phrase text="on" as="sí" />    <qg:phrase text="off" as="no" />    <qg:phrase text="Back" as="Volver" /></qg:language>  <!-- The title: both colours on one board, or white and black on two machines. -->  <qg:scene name="title" width="496" height="540" background="#2a2520">    <qg:hud position="top-center" size="40"><qg:text value="{'CHESS'}" /></qg:hud>    <qg:lobby local="game" local-label="Two players, one board" size="18" options="options" />  </qg:scene>  <qg:scene name="game" width="496" height="540" background="#2a2520">    <!-- row 0 is the eighth rank (black's home), row 7 the first; a square is board[row * 8 + col] -->    <q:set name="board" type="array" value="{['bR','bN','bB','bQ','bK','bB','bN','bR', 'bP','bP','bP','bP','bP','bP','bP','bP', '','','','','','','','', '','','','','','','','', '','','','','','','','', '','','','','','','','', 'wP','wP','wP','wP','wP','wP','wP','wP', 'wR','wN','wB','wQ','wK','wB','wN','wR']}" />    <q:set name="turn" value="w" />    <q:set name="sel_r" value="-1" type="number" />    <q:set name="sel_c" value="-1" type="number" />    <q:set name="legal" type="array" value="{[]}" />    <q:set name="ep_col" value="-1" type="number" />    <q:set name="moved" type="array" value="{[]}" />    <q:set name="status" value="White to move - click a piece, then where it goes" />    <q:set name="finished" value="false" type="boolean" />    <qg:tilemap tileset="board">1,2,1,2,1,2,1,22,1,2,1,2,1,2,11,2,1,2,1,2,1,22,1,2,1,2,1,2,11,2,1,2,1,2,1,22,1,2,1,2,1,2,11,2,1,2,1,2,1,22,1,2,1,2,1,2,1    </qg:tilemap>    <!-- the pointer: the mouse, or the arrows (Enter picks and puts); drawn as a frame round its square -->    <qg:cursor player="1" grid="62" sheet="board" frame="5" />    <qg:cursor player="2" grid="62" />    <!-- the marks, kept off the board until they mean something; under the pieces -->    <qg:instance prefab="Last" name="last_from" x="-100" y="-100" />    <qg:instance prefab="Last" name="last_to" x="-100" y="-100" />    <qg:instance prefab="Check" name="check_mark" x="-100" y="-100" />    <qg:instance prefab="Marker" name="mark" x="-100" y="-100" />    <qg:instance prefab="bR" x="31" y="31" /><qg:instance prefab="bN" x="93" y="31" /><qg:instance prefab="bB" x="155" y="31" /><qg:instance prefab="bQ" x="217" y="31" />    <qg:instance prefab="bK" x="279" y="31" /><qg:instance prefab="bB" x="341" y="31" /><qg:instance prefab="bN" x="403" y="31" /><qg:instance prefab="bR" x="465" y="31" />    <qg:instance prefab="bP" x="31" y="93" /><qg:instance prefab="bP" x="93" y="93" /><qg:instance prefab="bP" x="155" y="93" /><qg:instance prefab="bP" x="217" y="93" />    <qg:instance prefab="bP" x="279" y="93" /><qg:instance prefab="bP" x="341" y="93" /><qg:instance prefab="bP" x="403" y="93" /><qg:instance prefab="bP" x="465" y="93" />    <qg:instance prefab="wP" x="31" y="403" /><qg:instance prefab="wP" x="93" y="403" /><qg:instance prefab="wP" x="155" y="403" /><qg:instance prefab="wP" x="217" y="403" />    <qg:instance prefab="wP" x="279" y="403" /><qg:instance prefab="wP" x="341" y="403" /><qg:instance prefab="wP" x="403" y="403" /><qg:instance prefab="wP" x="465" y="403" />    <qg:instance prefab="wR" x="31" y="465" /><qg:instance prefab="wN" x="93" y="465" /><qg:instance prefab="wB" x="155" y="465" /><qg:instance prefab="wQ" x="217" y="465" />    <qg:instance prefab="wK" x="279" y="465" /><qg:instance prefab="wB" x="341" y="465" /><qg:instance prefab="wN" x="403" y="465" /><qg:instance prefab="wR" x="465" y="465" />    <qg:hud position="bottom-center" size="14"><qg:text bind="status" /></qg:hud>    <!-- what is on a square ("x" off the board), whose it is -->    <q:function name="at" params="r, c">      <q:if condition="{r &lt; 0 or r &gt; 7 or c &lt; 0 or c &gt; 7}"><q:return value="{'x'}" /></q:if>      <q:return value="{board[r * 8 + c]}" />    </q:function>    <q:function name="own" params="p, color">      <q:return value="{p != '' and p != 'x' and p[0:1] == color}" />    </q:function>    <q:function name="enemy" params="p, color">      <q:return value="{p != '' and p != 'x' and p[0:1] != color}" />    </q:function>    <q:function name="other_color" params="color">      <q:return value="{'b' if color == 'w' else 'w'}" />    </q:function>    <!-- where a piece may go by its own rule (attacks only, for a pawn, when atk) -->    <q:function name="pseudo" params="r, c, atk">      <q:set name="result" value="{[]}" />      <q:set name="p" value="{at(r, c)}" />      <q:set name="color" value="{p[0:1]}" />      <q:set name="kind" value="{p[1:2]}" />      <q:if condition="{kind == 'P'}">        <q:set name="dir" value="{-1 if color == 'w' else 1}" />        <q:if condition="{not atk and at(r + dir, c) == ''}">          <q:set name="result" value="{result + [[r + dir, c]]}" />          <q:if condition="{(r == 6 and color == 'w' or r == 1 and color == 'b') and at(r + 2 * dir, c) == ''}">            <q:set name="result" value="{result + [[r + 2 * dir, c]]}" />          </q:if>        </q:if>        <q:loop var="dc" items="{[-1, 1]}">          <q:if condition="{atk or enemy(at(r + dir, c + dc), color)}">            <q:if condition="{at(r + dir, c + dc) != 'x'}">              <q:set name="result" value="{result + [[r + dir, c + dc]]}" />            </q:if>          </q:if>          <q:if condition="{not atk and ep_col == c + dc and r == (3 if color == 'w' else 4)}">            <q:set name="result" value="{result + [[r + dir, c + dc]]}" />          </q:if>        </q:loop>      </q:if>      <q:if condition="{kind == 'N' or kind == 'K'}">        <q:set name="steps" value="{[[1, 2], [2, 1], [2, -1], [1, -2], [-1, -2], [-2, -1], [-2, 1], [-1, 2]] if kind == 'N' else [[1, 0], [1, 1], [0, 1], [-1, 1], [-1, 0], [-1, -1], [0, -1], [1, -1]]}" />        <q:loop var="s" items="{steps}">          <q:set name="q" value="{at(r + s[0], c + s[1])}" />          <q:if condition="{q != 'x' and not own(q, color)}">            <q:set name="result" value="{result + [[r + s[0], c + s[1]]]}" />          </q:if>        </q:loop>      </q:if>      <q:if condition="{kind == 'R' or kind == 'B' or kind == 'Q'}">        <q:set name="dirs" value="{[]}" />        <q:if condition="{kind != 'B'}"><q:set name="dirs" value="{dirs + [[1, 0], [-1, 0], [0, 1], [0, -1]]}" /></q:if>        <q:if condition="{kind != 'R'}"><q:set name="dirs" value="{dirs + [[1, 1], [1, -1], [-1, 1], [-1, -1]]}" /></q:if>        <q:loop var="d" items="{dirs}">          <q:set name="blocked" value="false" />          <q:loop var="n" from="1" to="7">            <q:if condition="{not blocked}">              <q:set name="q" value="{at(r + d[0] * n, c + d[1] * n)}" />              <q:if condition="{q == 'x' or own(q, color)}">                <q:set name="blocked" value="true" />              <q:else>                <q:set name="result" value="{result + [[r + d[0] * n, c + d[1] * n]]}" />                <q:if condition="{q != ''}"><q:set name="blocked" value="true" /></q:if>              </q:else>              </q:if>            </q:if>          </q:loop>        </q:loop>      </q:if>      <q:return value="{result}" />    </q:function>    <q:function name="attacked" params="r, c, by">      <q:loop var="i" from="0" to="63">        <q:if condition="{own(board[i], by)}">          <q:if condition="{[r, c] in pseudo(i // 8, i % 8, true)}"><q:return value="{true}" /></q:if>        </q:if>      </q:loop>      <q:return value="{false}" />    </q:function>    <q:function name="in_check" params="color">      <q:loop var="i" from="0" to="63">        <q:if condition="{board[i] == color + 'K'}">          <q:return value="{attacked(i // 8, i % 8, other_color(color))}" />        </q:if>      </q:loop>      <q:return value="{false}" />    </q:function>    <!-- the board after a move: the rook of a castling, the pawn of an en passant, a promotion -->    <q:function name="apply" params="r1, c1, r2, c2">      <q:set name="p" value="{board[r1 * 8 + c1]}" />      <q:if condition="{p[1:2] == 'P' and c1 != c2 and board[r2 * 8 + c2] == ''}">        <q:set name="board" index="{r1 * 8 + c2}" value="" />      </q:if>      <q:if condition="{p[1:2] == 'K' and c2 - c1 == 2}">        <q:set name="board" index="{r1 * 8 + 5}" value="{board[r1 * 8 + 7]}" />        <q:set name="board" index="{r1 * 8 + 7}" value="" />      </q:if>      <q:if condition="{p[1:2] == 'K' and c1 - c2 == 2}">        <q:set name="board" index="{r1 * 8 + 3}" value="{board[r1 * 8]}" />        <q:set name="board" index="{r1 * 8}" value="" />      </q:if>      <q:set name="board" index="{r1 * 8 + c1}" value="" />      <q:set name="board" index="{r2 * 8 + c2}" value="{p[0:1] + 'Q' if p[1:2] == 'P' and (r2 == 0 or r2 == 7) else p}" />    </q:function>    <!-- the moves that leave the king safe, castling included -->    <q:function name="legal_moves" params="r, c">      <q:set name="p" value="{at(r, c)}" />      <q:set name="color" value="{p[0:1]}" />      <q:set name="candidates" value="{pseudo(r, c, false)}" />      <q:if condition="{p[1:2] == 'K' and not (color + 'K') in moved and not in_check(color)}">        <q:if condition="{not (color + 'Rh') in moved and at(r, 5) == '' and at(r, 6) == '' and at(r, 7) == color + 'R' and not attacked(r, 5, other_color(color)) and not attacked(r, 6, other_color(color))}">          <q:set name="candidates" value="{candidates + [[r, 6]]}" />        </q:if>        <q:if condition="{not (color + 'Ra') in moved and at(r, 1) == '' and at(r, 2) == '' and at(r, 3) == '' and at(r, 0) == color + 'R' and not attacked(r, 3, other_color(color)) and not attacked(r, 2, other_color(color))}">          <q:set name="candidates" value="{candidates + [[r, 2]]}" />        </q:if>      </q:if>      <q:set name="result" value="{[]}" />      <q:loop var="m" items="{candidates}">        <q:set name="saved" value="{board[:]}" />        <q:call function="apply" args="r, c, m[0], m[1]" />        <q:set name="ok" value="{not in_check(color)}" />        <q:set name="board" value="{saved}" />        <q:if condition="{ok}"><q:set name="result" value="{result + [m]}" /></q:if>      </q:loop>      <q:return value="{result}" />    </q:function>    <q:function name="any_legal" params="color">      <q:loop var="i" from="0" to="63">        <q:if condition="{own(board[i], color)}">          <q:if condition="{len(legal_moves(i // 8, i % 8)) &gt; 0}"><q:return value="{true}" /></q:if>        </q:if>      </q:loop>      <q:return value="{false}" />    </q:function>    <q:function name="clear_mark">      <qg:put target="mark" x="-100" y="-100" />      <q:loop var="m" items="{legal}">        <q:set name="d" value="{thing_at('dot', m[1] * 62 + 31, m[0] * 62 + 31)}" />        <q:if condition="{d != null}"><qg:destroy target="d" /></q:if>      </q:loop>      <q:set name="sel_r" value="-1" />      <q:set name="sel_c" value="-1" />      <q:set name="legal" value="{[]}" />    </q:function>    <!-- the red glow under the king of the side to move, when it is in check -->    <q:function name="show_check">      <qg:put target="check_mark" x="-100" y="-100" />      <q:if condition="{in_check(turn)}">        <q:loop var="i" from="0" to="63">          <q:if condition="{board[i] == turn + 'K'}">            <qg:put target="check_mark" x="{(i % 8) * 62 + 31}" y="{(i // 8) * 62 + 31}" />          </q:if>        </q:loop>      </q:if>    </q:function>    <!-- a click: pick up one of your pieces, or put the picked one on a legal square. On one machine         the same pointer plays both colours; over the network each machine plays its own. -->    <qg:on-select>      <q:if condition="{not finished and (net_status() != 'playing' or cursor.player == 1 and turn == 'w' or cursor.player == 2 and turn == 'b')}">        <q:set name="r" value="{cursor.row}" />        <q:set name="c" value="{cursor.col}" />        <q:set name="act" value="{'move' if sel_r &gt;= 0 and [r, c] in legal else ('pick' if own(at(r, c), turn) else 'clear')}" />        <q:if condition="{act == 'move'}">          <q:set name="mover" value="{thing_at('piece', sel_c * 62 + 31, sel_r * 62 + 31)}" />          <q:set name="p" value="{board[sel_r * 8 + sel_c]}" />          <!-- what the move takes: the piece on the square, or the pawn passed en passant -->          <q:set name="taken" value="{thing_at('piece', c * 62 + 31, r * 62 + 31)}" />          <q:if condition="{taken == null and p[1:2] == 'P' and c != sel_c}">            <q:set name="taken" value="{thing_at('piece', c * 62 + 31, sel_r * 62 + 31)}" />          </q:if>          <q:if condition="{taken != null}"><qg:destroy target="taken" /></q:if>          <q:if condition="{p[1:2] == 'K' and c - sel_c == 2}">            <q:set name="taken" value="{thing_at('piece', 7 * 62 + 31, r * 62 + 31)}" />            <qg:put target="taken" x="{5 * 62 + 31}" y="{r * 62 + 31}" />          </q:if>          <q:if condition="{p[1:2] == 'K' and sel_c - c == 2}">            <q:set name="taken" value="{thing_at('piece', 31, r * 62 + 31)}" />            <qg:put target="taken" x="{3 * 62 + 31}" y="{r * 62 + 31}" />          </q:if>          <qg:put target="mover" x="{c * 62 + 31}" y="{r * 62 + 31}" />          <q:if condition="{p[1:2] == 'P' and (r == 0 or r == 7)}">            <q:if condition="{turn == 'w'}"><qg:swap target="mover" prefab="wQ" /><q:else><qg:swap target="mover" prefab="bQ" /></q:else></q:if>          </q:if>          <q:call function="apply" args="sel_r, sel_c, r, c" />          <!-- the rights and the en passant square after the move -->          <q:if condition="{p[1:2] == 'K'}"><q:set name="moved" value="{moved + [turn + 'K']}" /></q:if>          <q:if condition="{p[1:2] == 'R' and sel_c == 0}"><q:set name="moved" value="{moved + [turn + 'Ra']}" /></q:if>          <q:if condition="{p[1:2] == 'R' and sel_c == 7}"><q:set name="moved" value="{moved + [turn + 'Rh']}" /></q:if>          <q:set name="ep_col" value="{c if p[1:2] == 'P' and abs(r - sel_r) == 2 else -1}" />          <qg:put target="last_from" x="{sel_c * 62 + 31}" y="{sel_r * 62 + 31}" />          <qg:put target="last_to" x="{c * 62 + 31}" y="{r * 62 + 31}" />          <q:set name="turn" value="{other_color(turn)}" />          <q:call function="clear_mark" />          <q:call function="show_check" />          <q:set name="status" value="{('White' if turn == 'w' else 'Black') + ' to move'}" />          <q:if condition="{not any_legal(turn)}">            <q:set name="finished" value="true" />            <q:set name="status" value="{('Checkmate - ' + ('Black' if turn == 'w' else 'White') + ' wins') if in_check(turn) else 'Stalemate'}" />          <q:elseif condition="{in_check(turn)}">            <q:set name="status" value="{status + ' - check'}" />          </q:elseif>          </q:if>        </q:if>        <q:if condition="{act == 'pick'}">          <q:call function="clear_mark" />          <q:set name="sel_r" value="{r}" />          <q:set name="sel_c" value="{c}" />          <q:set name="legal" value="{legal_moves(r, c)}" />          <qg:put target="mark" x="{cursor.x}" y="{cursor.y}" />          <q:loop var="m" items="{legal}">            <q:if condition="{at(m[0], m[1]) == ''}">              <qg:spawn prefab="Dot" x="{m[1] * 62 + 31}" y="{m[0] * 62 + 31}" />            <q:else>              <qg:spawn prefab="Ring" x="{m[1] * 62 + 31}" y="{m[0] * 62 + 31}" />            </q:else>            </q:if>          </q:loop>          <q:set name="status" value="{('White' if turn == 'w' else 'Black') + (' to move' if len(legal) &gt; 0 else ' to move - that piece cannot move')}" />          <q:if condition="{len(legal) &gt; 0 and in_check(turn)}"><q:set name="status" value="{status + ' - check'}" /></q:if>        </q:if>        <q:if condition="{act == 'clear'}">          <q:call function="clear_mark" />        </q:if>      </q:if>    </qg:on-select>  </qg:scene>  <!-- ===== Options: how loud, the full screen, the keys; kept between runs ===== -->  <qg:scene name="options" width="496" height="540" background="#2a2520">    <qg:hud position="top-center" size="36"><qg:text value="{tr('OPTIONS')}" /></qg:hud>    <qg:menu position="center" size="18">      <qg:button label="{tr('Full screen') + ': ' + (tr('on') if fullscreen() else tr('off'))}"><qg:fullscreen /></qg:button>      <qg:button label="Back"><qg:goto-scene name="title" /></qg:button>    </qg:menu>  </qg:scene></q:application>