Newest

1
export { default } from "https://esm.town/v/nbbaier/sqliteExplorerApp?v=81";

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";
let o = 1;
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);

The web game

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
/** @jsxImportSource npm:hono@3/jsx */
import { wordsMatching } from "https://esm.town/v/jdan/wordsMatching";
import { Hono } from "npm:hono@3";
function esmTown(url) {
return fetch(url, {
headers: {
"User-Agent":
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.142.86 Safari/537.36",
},
}).then(r => r.text());
}
const app = new Hono();
export default app.fetch;
app.get("/:letters", async (c) => {
const letters = c.req.param("letters");
const matchingWords = await wordsMatching(letters);
return c.html(
<html>
<head>
<title>Word game</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
{/* <script src="https://cdn.tailwindcss.com" /> */}
<style
dangerouslySetInnerHTML={{
__html: `
ul {
display: flex;
flex-flow: wrap column;
max-height: 150px; /* Limit height to whatever you need */
}`,
}}
/>
</head>
<body>
<div>
Find all the words using the letters: {letters.toUpperCase()}
</div>
<div id="state"></div>
<form id="form">
<input type="text" id="input" style={{ textTransform: "uppercase" }} />
<button type="submit">Submit</button>
</form>
<script
dangerouslySetInnerHTML={{
__html: `window.words = ${JSON.stringify(matchingWords)}`,
}}
type="module"
/>
<script
dangerouslySetInnerHTML={{
__html: await esmTown("https://esm.town/v/jdan/gameScript"),
}}
type="module"
/>
</body>
</html>,
);
});
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
/** @jsxImportSource npm:react **/
import { renderToString } from "npm:react-dom@18/server";
export default (req: Request) => {
return new Response(
renderToString(
<html>
<head>
<title>Guillermo Rauch's val</title>
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000" />
<style>
{`* { background: #000; color: #fff; margin: 0; padding: 0 }
main { width: 100dvw; height: 100dvh; display: flex; justify-content: center; align-items: center; }`}
</style>
</head>
<main>
<a href="https://vercel.com" target="_blank">
<svg
width="76"
height="65"
viewBox="0 0 76 65"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M37.5274 0L75.0548 65H0L37.5274 0Z"
fill="#fff"
/>
</svg>
</a>
</main>
</html>,
),
{ headers: { "Content-Type": "text/html" } },
);
};
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
import { getAllWords } from "https://esm.town/v/jdan/getAllWords";
export async function wordsMatching(input: string) {
// the count of each letter in the input
const inputCount = input
.split("")
.reduce((acc, letter) => {
acc[letter] = (acc[letter] || 0) + 1;
return acc;
}, {} as Record<string, number>);
const allWords = await getAllWords();
return allWords.filter((word) => {
// return true if `word` can be made out of the letters in `input`
// Optimization: index words this way when populating the dictionary
const wordCount = word
.split("")
.reduce((acc, letter) => {
acc[letter] = (acc[letter] || 0) + 1;
return acc;
}, {} as Record<string, number>);
return Object.keys(wordCount).every((letter) => {
return inputCount[letter] >= wordCount[letter];
});
}).sort((a, b) => {
// Sort shortest first, then alphabetically
if (a.length > b.length) {
return 1;
} else if (a.length < b.length) {
return -1;
} else {
return a < b ? -1 : 1;
}
});
}
1
2
3
4
5
6
import { blob } from "https://esm.town/v/std/blob?v=12";
export async function getAllWords() {
const allWords = await blob.getJSON("words");
return allWords;
}

Hello val town!!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/** @jsxImportSource npm:react **/
import { renderToString } from "npm:react-dom@18/server";
export default (req: Request) => {
return new Response(
renderToString(
<html>
<link rel="stylesheet" href="https://unpkg.com/missing.css@1.1.1" />
<main>
<h1>Hello gueejla!</h1>
<p>This is your first val. Journey of a thousand vals begins with a single line of code.</p>
</main>
</html>,
),
{ headers: { "Content-Type": "text/html" } },
);
};
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
export const pyodideExample = () => {
const html = `
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Machine Learning with Pyodide</title>
<script type="text/javascript" src="https://cdn.jsdelivr.net/pyodide/v0.25.1/full/pyodide.js"></script>
<script src="https://cdn.plot.ly/plotly-latest.min.js"></script>
</head>
<body>
<h1>Train and Test a Linear Regression Model using Python in WebAssembly</h1>
<input type="file" id="fileInput" accept=".csv">
<button id="trainModel">Train Model</button>
<div id="modelOptions" style="display: none;">
<h3>Select Features and Target</h3>
<label for="featureSelect">Select Feature:</label>
<select id="featureSelect"></select>
<label for="targetSelect">Select Target:</label>
<select id="targetSelect"></select>
<button id="train">Train</button>
</div>
<div id="testDataOptions" style="display: none;">
<h3>Upload Test Data</h3>
<input type="file" id="testDataFile" accept=".csv">
<button id="predict">Predict</button>
</div>
<div id="result"></div>
<div id="plotContainer"></div>
<script>
async function main() {
let pyodide = await loadPyodide();
await pyodide.loadPackage(['numpy', 'pandas', 'scikit-learn', 'matplotlib']);
console.log('Pyodide is ready to use!');
pyodide.runPython(\`
import pandas as pd
import numpy as np
import json
from sklearn.linear_model import LinearRegression
import base64
import io
import matplotlib.pyplot as plt
class LinearModel:
def __init__(self):
self.model = None
self.feature = None
self.target = None
self.df = None
def load_csv(self, file_content):
self.df = pd.read_csv(io.StringIO(file_content))
return self.df.to_json()
def get_columns(self):
return json.dumps(self.df.columns.tolist())
def train_model(self, feature, target):
self.feature = feature
self.target = target
X = self.df[[feature]].values
y = self.df[target].values
self.model = LinearRegression()
self.model.fit(X, y)
predictions = self.model.predict(X)
plt.figure()
plt.scatter(X, y, color='blue', label='Actual')
plt.plot(X, predictions, color='red', label='Predicted')
plt.xlabel(feature)
plt.ylabel(target)
plt.title('Linear Regression')
plt.legend()
buf = io.BytesIO()
plt.savefig(buf, format='png')
buf.seek(0)
img = base64.b64encode(buf.read()).decode('utf-8')
return img, self.model.coef_[0], self.model.intercept_
def predict(self, test_file_content):
test_df = pd.read_csv(io.StringIO(test_file_content))
X_test = test_df[self.feature].values.reshape(-1, 1)
predictions = self.model.predict(X_test)
return json.dumps({"Experience": X_test.flatten().tolist(), "Predicted": predictions.tolist()})
linear_model = LinearModel()
\`);
return pyodide;
}
const pyodide = main();
let jsonData;
const fileInput = document.getElementById('fileInput');
const trainModelButton = document.getElementById('trainModel');
const modelOptions = document.getElementById('modelOptions');
const featureSelect = document.getElementById('featureSelect');
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
export const pyodideExample = () => {
const html = `
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Data Analysis with Pyodide</title>
<script type="text/javascript" src="https://cdn.jsdelivr.net/pyodide/v0.25.1/full/pyodide.js"></script>
<script src="https://cdn.plot.ly/plotly-latest.min.js"></script>
</head>
<body>
<h1>Data Analysis using Python in WebAssembly</h1>
<input type="file" id="fileInput" accept=".csv">
<button id="analyze">Analyze</button>
<div id="analysisOptions" style="display: none;">
<h3>Select Analysis</h3>
<label for="columnSelect">Select Column:</label>
<select id="columnSelect"></select>
<button id="plotHistogram">Plot Histogram</button>
<button id="calculateStats">Calculate Statistics</button>
</div>
<div id="result"></div>
<div id="plotContainer"></div>
<script>
async function main() {
let pyodide = await loadPyodide();
await pyodide.loadPackage(['numpy', 'pandas', 'matplotlib']);
console.log('Pyodide is ready to use!');
pyodide.runPython(\`
import pandas as pd
import numpy as np
import json
import base64
import io
import matplotlib.pyplot as plt
def load_csv(file_content):
df = pd.read_csv(io.StringIO(file_content))
return df.to_json()
def get_columns(json_df):
df = pd.read_json(json_df)
return json.dumps(df.columns.tolist())
def calculate_statistics(json_df, column):
df = pd.read_json(json_df)
stats = df[column].describe().to_dict()
return json.dumps(stats)
def plot_histogram(json_df, column):
df = pd.read_json(json_df)
plt.figure()
df[column].hist(bins=30)
plt.xlabel(column)
plt.ylabel('Frequency')
plt.title(f'Histogram of {column}')
buf = io.BytesIO()
plt.savefig(buf, format='png')
buf.seek(0)
img = base64.b64encode(buf.read()).decode('utf-8')
return img
\`);
return pyodide;
}
const pyodide = main();
let jsonData;
const fileInput = document.getElementById('fileInput');
const analyzeButton = document.getElementById('analyze');
const analysisOptions = document.getElementById('analysisOptions');
const columnSelect = document.getElementById('columnSelect');
const resultElement = document.getElementById('result');
const plotContainer = document.getElementById('plotContainer');
analyzeButton.addEventListener('click', async () => {
const file = fileInput.files[0];
const reader = new FileReader();
reader.onload = async (e) => {
const fileContent = e.target.result;
jsonData = await pyodide.then(pyodide => pyodide.runPython(\`load_csv("""\${fileContent}""")\`));
const columns = await pyodide.then(pyodide => pyodide.runPython(\`get_columns(\${JSON.stringify(jsonData)})\`));
const columnsList = JSON.parse(columns);
columnSelect.innerHTML = '';
columnsList.forEach(col => {
const option = document.createElement('option');
option.value = col;
option.textContent = col;
columnSelect.appendChild(option);
});
analysisOptions.style.display = 'block';
};
reader.readAsText(file);
});
document.getElementById('calculateStats').addEventListener('click', async () => {
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
export const pyodideExample = () => {
const html = `
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Data Vis with Pyodide</title>
<script type="text/javascript" src="https://cdn.jsdelivr.net/pyodide/v0.25.1/full/pyodide.js"></script>
<script src="https://cdn.plot.ly/plotly-latest.min.js"></script>
</head>
<body>
<h1>Data Vis using Python in WebAssembly</h1>
<input type="text" id="numbers" placeholder="Enter a list of numbers, separated by commas">
<button id="plot">Plot</button>
<div id="plotContainer"></div>
<script>
async function main() {
let pyodide = await loadPyodide();
await pyodide.loadPackage('numpy');
console.log('Pyodide is ready to use!');
pyodide.runPython(\`
import numpy as np
import json
def plot_numbers(numbers):
x = np.array(numbers)
y_square = x ** 2
y_cube = x ** 3
data = {
'x': x.tolist(),
'y_square': y_square.tolist(),
'y_cube': y_cube.tolist()
}
return json.dumps(data)
\`);
return pyodide;
}
const pyodide = main();
const plotButton = document.getElementById('plot');
const numbersInput = document.getElementById('numbers');
const plotContainer = document.getElementById('plotContainer');
plotButton.addEventListener('click', async () => {
const numbers = numbersInput.value.split(',').map(Number);
const plotData = await pyodide.then(pyodide => pyodide.runPython(\`plot_numbers(\${JSON.stringify(numbers)})\`));
const data = JSON.parse(plotData);
const trace1 = {
x: data.x,
y: data.y_square,
mode: 'lines+markers',
name: 'Square'
};
const trace2 = {
x: data.x,
y: data.y_cube,
mode: 'lines+markers',
name: 'Cube'
};
const layout = {
title: 'Numbers and Their Powers',
xaxis: { title: 'Number' },
yaxis: { title: 'Value' }
};
Plotly.newPlot(plotContainer, [trace1, trace2], layout);
});
</script>
</body>
</html>
`;
return new Response(html, {
headers: {
"Content-Type": "text/html",
},
});
};