Readme

Play connect4.

  • Write agents that play connect4.
  • Battle your agents against other agents.
  • It's fun.
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/hono/jsx */
import { track } from "https://esm.town/v/saolsen/plausible?v=3";
import { init, traced_fetch, traced_handler, traced, get_tracer } from "https://esm.town/v/saolsen/tracing?v=135";
import { trace } from "npm:@opentelemetry/api";
init("connect4_site");
const tracer = get_tracer("connect4_site");
import { blob } from "https://esm.town/v/std/blob";
import { customAlphabet } from "npm:nanoid";
import { z } from "npm:zod";
import { Hono } from "npm:hono";
import { html } from "npm:hono/html";
import { jsxRenderer, useRequestContext } from "npm:hono/jsx-renderer";
import * as connect4 from "https://esm.town/v/saolsen/connect4";
// TODO:
// * htmx loading spinners
const _nanoid = customAlphabet("123456789abcdefghijklmnopqrstuvwxyz", 10);
const MatchId = z.string().startsWith("m_");
type MatchId = z.infer<typeof MatchId>;
function matchid(): MatchId {
return `m_${_nanoid()}`;
}
type MePlayer = {
kind: "me";
};
type AgentPlayer = {
kind: "agent";
name: string;
url: string;
};
type Player = MePlayer | AgentPlayer;
type AgentFetchError = {
kind: "agent_fetch";
error_message: string;
};
type AgentHTTPResponseError = {
kind: "agent_http_response";
status: number;
body: string;
};
type AgentInvalidActionJsonError = {
kind: "agent_invalid_json";
body: string;
parse_error: string;
};
type AgentInvalidActionError = {
kind: "agent_invalid_action";
action: connect4.Action;
error: string;
};
type Error =
| AgentFetchError
| AgentHTTPResponseError
| AgentInvalidActionJsonError
| AgentInvalidActionError;
type ErrorStatus = {
status: "error";
error: Error;
};
type StatusOrError = connect4.Status | ErrorStatus;
type Turn = {
number: number;
status: StatusOrError;
player: number | null;
column: number | null;
state: connect4.State;
};
type Match = {
id: MatchId;
players: [Player, Player];
turns: Turn[];
};
async function get_match(id: MatchId): Promise<Match | null> {
const span = tracer.startSpan("get_match");
span.setAttribute("match_id", id);
const match = await blob.getJSON(`connect4_matches/${id}`);
span.end();
if (match === undefined) {
return null;
}
return match;
}
👆 This is a val. Vals are TypeScript snippets of code, written in the browser and run on our servers. Create scheduled functions, email yourself, and persist small pieces of data — all from the browser.