Newest

Static Chess

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 { 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> {
const gameInfo = parseURL(req.url);
if (gameInfo === undefined) {
return new Response("Not Found", { status: 404, headers: { "cache-control": "max-age=86400, public" } });
}
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>
<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", "cache-control": "max-age=86400, public" } },
);
}
}
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;
let className = "square";
if (color) className += " " + color;
if (this.selected) {
if (this.nextMoves[pos]) className += " highlight";
if (this.selected == pos) className += " selected highlight";
}
const squareContent = (() => {
if (this.selectable.includes(pos)) {
return <a href={`/${this.fen}/${pos}`}>{pieces[this.board[row][square]?.type]}</a>;
}
const nextMove = this.nextMoves[pos];
if (nextMove !== undefined) {
return (
<a href={`/${nextMove.after.replaceAll(" ", "_")}/`}>
{pieces[this.board[row][square]?.type]}
</a>
);
}
return <span>{pieces[this.board[row][square]?.type]}</span>;
})();
1
2
3
4
5
6
7
8
9
10
import { fetchText } from "https://esm.town/v/stevekrouse/fetchText?v=6";
import { html, load } from "npm:cheerio";
const htmlStr = await fetchText(
"https://archive.is/pPFRB",
);
const $ = load(htmlStr);
// Cheerio accepts a CSS selector, here we pick the second <p>
const intro = $.html();
console.log(intro);

Static Chess

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 { 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> {
const gameInfo = parseURL(req.url);
if (gameInfo === undefined) {
return new Response("Not Found", { status: 404, headers: { "cache-control": "max-age=86400, public" } });
}
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>
<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", "cache-control": "max-age=86400, public" } },
);
}
}
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;
let className = "square";
if (color) className += " " + color;
if (this.selected) {
if (this.nextMoves[pos]) className += " highlight";
if (this.selected == pos) className += " selected highlight";
}
const squareContent = (() => {
if (this.selectable.includes(pos)) {
return <a href={`/${this.fen}/${pos}`}>{pieces[this.board[row][square]?.type]}</a>;
}
const nextMove = this.nextMoves[pos];
if (nextMove !== undefined) {
return (
<a href={`/${nextMove.after.replaceAll(" ", "_")}/`}>
{pieces[this.board[row][square]?.type]}
</a>
);
}
return <span>{pieces[this.board[row][square]?.type]}</span>;
})();
1
2
3
4
5
6
7
8
9
10
11
12
import { S3Client } from "https://deno.land/x/s3_lite_client@0.6.1/mod.ts"
const s3client = new S3Client({
endPoint: "s3.amazonaws.com",
region: "us-east-1",
bucket: "test-bucket",
accessKey: Deno.env.get("awsS3Key"),
secretKey: Deno.env.get("awsS3Secret"),
});
const upload = await s3client.putObject("filename.txt", "File contents");
console.log(upload)

Import a val script in the chrome console

Say you're developing a chrome extension or some other little script and you're iterating in the chrome console. Over time you may write larger pieces of code that you want to save as a group. Or maybe you want to use TypeScript, which isn't supported in browsers natively. Val Town can provide a decent workflow here.

  1. Save your code in a Script val
  2. Ensure the val is Public or Unlisted
  3. In the chrome console, import your val's module url. For example, for this val it would look like this:
Create vallet {test} = await import(`https://esm.town/v/stevekrouse/importValInChromeConsole?${Math.random()}`)

I added a random number to the end of the string to prevent your browser from caching the import. So now you can save in Val Town and then re-run your import line in the Chrome console and the script will re-run in the Chrome console.

1
2
3
4
5
console.log("This runs on import");
export function test() {
console.log("This runs when test() is called");
}

Run squint in val.town!

1
2
3
4
5
6
7
8
9
import * as squint from "npm:squint-cljs";
globalThis.squint_core = squint;
const program = `(js/console.log (+ 1 2 3))`;
let theString = squint.compileString(program, { repl: true, "elide-imports": true });
console.log(theString);
theString = theString.replace(`'squint-cljs/core.js'`, `'npm:squint-cljs/core.js'`);
const AsyncFunction = Object.getPrototypeOf(async function() {}).constructor;
await new AsyncFunction(theString)();

OpenAI Proxy

This OpenAI API proxy injects Val Town's API keys. For usage documentation, check out https://www.val.town/v/std/openai

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
import { parseBearerString } from "https://esm.town/v/andreterron/parseBearerString";
import { API_URL } from "https://esm.town/v/std/API_URL?v=5";
import { RateLimit } from "npm:@rlimit/http";
const allowedPathnames = [
"/",
"/v1/chat/completions",
];
const rlimit = new RateLimit({
namespace: Deno.env.get("rlimit_namespace"),
maximum: 10,
interval: "1m",
});
export default async function(req: Request): Promise<Response> {
// Ensure only allowed pathnames are used
const { pathname, search } = new URL(req.url);
if (!allowedPathnames.includes(pathname)) {
return new Response("Path not supported", { status: 404 });
}
// Checks the user identity
const authHeader = req.headers.get("Proxy-Authorization") || req.headers.get("Authorization");
const token = authHeader ? parseBearerString(authHeader) : undefined;
const meRes = await fetch(`${API_URL}/v1/me`, { headers: { Authorization: `Bearer ${token}` } });
if (!meRes.ok) {
return new Response("Unauthorized", { status: 401 });
}
const user = await meRes.json();
// Check rate limit
const { ok } = await rlimit.check(`user:${user.id}`);
if (!ok) {
// Rate limited
return new Response("Too Many Requests", { status: 429 });
}
// Proxy the request
const url = new URL("." + pathname, "https://api.openai.com");
url.search = search;
const headers = new Headers(req.headers);
headers.set("Host", url.hostname);
headers.set("Authorization", `Bearer ${Deno.env.get("OPENAI_API_KEY")}`);
headers.set("OpenAI-Organization", Deno.env.get("OPENAI_API_ORG"));
const openAIRes = await fetch(url, {
method: req.method,
headers,
body: await limitFreeModel(req, user),
redirect: "manual",
});
const res = new Response(openAIRes.body, openAIRes);
// Remove internal header
res.headers.delete("openai-organization");
return res;
}
async function limitFreeModel(req: Request, user: any) {
if (user.tier === "pro") return req.body;
const input = await req.json();
console.log(input);
return JSON.stringify({
...input,
model: "gpt-3.5-turbo",
});
}
// Adapted from https://blog.r0b.io/post/creating-a-proxy-with-deno/

AQI Alerts

Get email alerts when AQI is unhealthy near you.

This val uses nominatim's geocoder to get your lat, lon, and air quality data from OpenAQ. It uses EPA's NowCast AQI Index calculation and severity levels. Learn more: https://www.val.town/v/stevekrouse.easyAQI

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import { email } from "https://esm.town/v/std/email?v=9";
import { easyAQI } from "https://esm.town/v/stevekrouse/easyAQI";
export async function aqi(interval: Interval) {
const location = "barcelona"; // <-- change to place, city, or zip code
const data = await easyAQI({ location });
if (!interval.lastRunAt) {
email({
text:
`You will now get Air Quality alerts for ${location} if it's unhealthy. It is now ${data.aqi} which is ${data.severity}.`,
subject: `AQI Alerts for ${location} setup!`,
});
}
if (data.severity.includes("Unhealthy")) {
email({
text: "Air Quality: " + data.severity,
subject: `AQI in ${location} is ${data.aqi}`,
});
}
}
1
2
3
4
5
6
7
8
9
10
11
12
import { chat } from "https://esm.town/v/yi_ge_dian/chat";
export const chatSampleDelimeter = (async () => {
const text = `
你应该提供尽可能清晰、具体的指示,以表达你希望模型执行的任务。
这将引导模型朝向所需的输出,并降低收到无关或不正确响应的可能性。
不要将写清晰的提示与写简短的提示混淆。
在许多情况下,更长的提示可以为模型提供更多的清晰度和上下文信息,从而导致更详细和相关的输出。
`;
const prompt = `把用三个双引号括起来的文本总结成一句话。"""${text}"""`;
return await chat(prompt);
})();
1
2
3
4
5
6
7
8
9
10
11
12
13
import { chat } from "https://esm.town/v/yi_ge_dian/chat";
export const chatSampleDelimeter = (async () => {
const text = `
你应该提供尽可能清晰、具体的指示,以表达你希望模型执行的任务。
这将引导模型朝向所需的输出,并降低收到无关或不正确响应的可能性。
不要将写清晰的提示与写简短的提示混淆。
在许多情况下,更长的提示可以为模型提供更多的清晰度和上下文信息,从而导致更详细和相关的输出。
`;
const prompt = `把用三个双引号括起来的文本总结成一句话。"""${text}"""`;
const result = await chat(prompt);
console.log(result);
})();