maxm-staticchess.web.val.run
Readme

Static Chess

Check it out here: https://chess.maxmcd.com

Plain, brutalist, no bloat chess. Every page is only html and css. Every chess move is made by clicking a link. Send a link to your friend and they'll send you one back to make your move. No silly animations or slick interactivity to trip up your gameplay. When Google indexes this site will we successfully compute all possible chess moves?

Functionality is quite limited, and things might be broken. Please let me know if you find bugs!

Inspired by this HN discussion about sites that have all possible game states of tic-tac-toe.

I plan on extending this to support real gameplay. I think it could be a nice simple interface for long form games with friends. Might also be fun to add a static AI to play against. Feel free to PR any changes if you'd like to see something added.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
/** @jsxImportSource https://esm.sh/react */
import { analyticsHandlerWrapper } from "https://esm.town/v/maxm/valTownAnalytics";
import { Chess, Move, Square } from "npm:chess.js";
import minify from "npm:css-simple-minifier";
import { renderToString } from "npm:react-dom/server";
class StaticChess {
size = 8;
rows = Array.from({ length: this.size }, (_, i) => i);
squares = Array.from({ length: this.size }, (_, i) => i);
constructor() {}
async fetch(req: Request): Promise<Response> {
if (new URL(req.url).pathname === "/robots.txt") {
return new Response("User-agent: *\nDisallow: /");
}
const gameInfo = parseURL(req.url);
if (gameInfo === undefined) {
return new Response("Not Found", { status: 404 });
}
const game = new Game(gameInfo.game, gameInfo.selected);
return new Response(
renderToString(
<html>
<head>
<title>Static Chess</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="icon" href="https://fav.farm/♟️" />
<style>{minify(CSS)}</style>
</head>
<body>
<div id="code-on-vt-host">
<link
rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/github-fork-ribbon-css/0.2.3/gh-fork-ribbon.min.css"
/>
<a
href="https://www.val.town/v/maxm/staticChess"
rel="source"
target="_blank"
className="github-fork-ribbon"
data-ribbon="Code on Val Town"
title="Code on Val Town"
>
Code on Val Town
</a>
</div>
<h1>Static Chess</h1>
<div>
<a href="https://www.val.town/v/maxm/staticChess">info</a> - <a href="/">reset</a>
</div>
<div className="board">
{this.rows.map(row => (
<div key={row} className="row">{this.squares.map(square => game.squareContent(row, square))}</div>
))}
</div>
<div className="info">
{game.selected
? "Click a highted square to move the selected piece, or select a different piece."
: `It is ${{ w: "white", b: "black" }[game.game.turn()]}'s turn. Click a piece to make a move.`}
</div>
</body>
</html>,
),
{ headers: { "content-type": "text/html; charset=utf-8;" } },
);
}
}
class Game {
game: Chess;
selected?: string;
selectable: string[];
board;
nextMoves: { [key: string]: Move };
fen: string;
constructor(game: Chess, selected?: string) {
this.game = game;
this.selected = selected;
this.board = game.board();
this.fen = game.fen().replaceAll(" ", "_");
this.nextMoves = {};
this.selectable = game.moves({ verbose: true }).map((m) => m.from.toString());
if (this.selected) {
var moves = game.moves({
square: selected as Square,
verbose: true,
});
for (const move of moves) {
this.nextMoves[move.to] = move;
}
}
}
squareContent(row: number, square: number) {
const pos = indexToPos(row, square);
const color = this.board[row][square]?.color;
Val Town is a social website to write and deploy JavaScript.
Build APIs and schedule functions from your browser.
Comments
7
tianshuo avatar

You did not check if it's checkmate/stalemate. For example after

  1. f3 e5
  2. g4 Qh4# It still says white to go.
maxm avatar

@tianshuo nice, thank you.

Also going to add this link here: https://lobste.rs/s/ttyfuk/static_chess#c_e0sq0q And this one: https://news.ycombinator.com/item?id=40348343

Should fix these bugs and consider making the described changes.

peterqliu avatar

my first reaction before clicking the link was "but does it have en passant"?

great work, nonetheless

nbbaier avatar

I might be missing something here, where is the import for the Game constructor used here: https://www.val.town/v/maxm/staticChess#L21

nbbaier avatar

And actually, for that matter, where is parseURL coming from? That doesn't seem to be defined either. (https://www.val.town/v/maxm/staticChess#L17)

maxm avatar

@nbbaier I tried to clean things up a little by breaking them out, but it does make the code a littler harder to understand. If you scroll past the StaticChess class Game and parseURL are defined lower down in the file.

nbbaier avatar

@maxm - ah yeah, now I see that. Don't know why I didn't just ⌘+F sigh

v26
June 24, 2024