Skip to content

Commit 56c110d

Browse files
Update README and some cleanup (#127)
* update readme * crop img * build server types * revert change to tsconfig
1 parent b621117 commit 56c110d

File tree

8 files changed

+241
-94
lines changed

8 files changed

+241
-94
lines changed

README.md

Lines changed: 126 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,138 @@
11
# Simon
22

3+
This is a case study in application architecture for the classic memory game Simon. The game requires the user to play back a computer generated sequence of notes that increases in length each turn.
4+
5+
Play now! https://simon.codenickycode.com
6+
7+
## Architecture
8+
39
![architecture diagram](./docs/simon-arch.png)
410

5-
# Debug
11+
### Front End
12+
13+
This application uses [React](https://react.dev) to separate data, logic, and visual elements into separate components. State is maintained via React hooks, and UI is rendered as html via jsx. Musical sequencing and synthesis is made possible by [Tone.js](https://tonejs.github.io/), an abstraction over the Web Audio API. It is deployed via [Cloudflare Pages](https://developers.cloudflare.com/pages/).
14+
15+
#### [Root](./client/src/simon.tsx)
16+
17+
The parent function that orchestrates all other components to fetch data, maintain state, and render UI. This is the root of our React application.
18+
19+
#### [API](./client//src/services/api.high-score.ts)
20+
21+
A service to retrieve and update the global high score from our back end API. This component stores the pending, success, and error states of all requests.
22+
23+
#### [Game State](./client/src/components/use-game-machine.ts)
24+
25+
A finite state machine that maintains the status of the game: New Game, Computer Turn, User Turn, Game Over. It uses the [state reducer pattern](https://kentcdodds.com/blog/the-state-reducer-pattern-with-react-hooks), accepting a single action to transition state and update the user's current score.
26+
27+
#### [Pad Controller](./client/src/components/use-pad-controller.ts)
28+
29+
A stateful hook that controls the "active" status of each pad and provides methods for accepting user and computer pad input.
30+
31+
#### [Melody Player](./client/src/services/melody-player.ts)
32+
33+
Preset melodies to play when entering the game over state.
34+
35+
#### [Sequencer](./client/src/services/sequencer.ts)
36+
37+
Builds a sequence of notes as the game progresses. These are the notes the user must repeat in order to continue playing. The sequencer plays tones using a synth and is observed by the pad controller to set the current playing pad as active.
38+
39+
#### Synths
40+
41+
These are `Tone.Synth` instances that receive input from the pad controller, melody player, and sequencer to emit tones to the audio device. See [Tone.js | Class Synth](https://tonejs.github.io/docs/15.0.4/classes/Synth.html)
42+
43+
### Back End
44+
45+
The back end is a simple [Hono](https://hono.dev/) REST API on [Cloudflare Workers](https://developers.cloudflare.com/workers/) and a [Cloudflare KV](https://developers.cloudflare.com/kv/) database. It exposes retrieving and updating a global high score.
46+
47+
# Development
48+
49+
## Getting Started
50+
51+
### Pre-requisites
652

7-
## Server starts on wrong port locally
53+
1. [pnpm](https://pnpm.io/installation) v9 and above
54+
2. [node](https://nodejs.org/en/download/package-manager/current) v22.9.0 and above
55+
56+
### Installation
57+
58+
1. Ensure you're using the correct version of Node:
59+
60+
```sh
61+
nvm use 22.9.0
62+
```
63+
64+
2. If necessary, install the correct version of pnpm:
65+
66+
```sh
67+
npm i -g pnpm@9
68+
```
69+
70+
3. Install dependencies
71+
72+
```sh
73+
pnpm i
74+
```
75+
76+
4. Start local dev server
77+
78+
```sh
79+
pnpm run dev
80+
```
81+
82+
### Deployment Configuration
83+
84+
1. Follow instructions in [README.cloudflare](./README.cloudflare/README.cloudflare.md)
85+
86+
2. Follow instructions in [README.sentry](./README.sentry/README.sentry.md)
87+
88+
### Deploying a branch to stage
89+
90+
1. Click the "Actions" tab
91+
2. Select the "stage" workflow
92+
3. Open the dropdown for "Run workflow" and select the branch you wish to deploy
93+
4. Choose your deploy target (client, server, both)
94+
5. Click "Run workflow"
95+
96+
The client app will deploy to the preview url, and the server will deploy to your staging worker.
97+
98+
## Scripts
99+
100+
- `pnpm run dev`: Start the development server
101+
- `pnpm run lint`: Run ESLint
102+
- `pnpm run test`: Run unit tests with Vitest
103+
- `pnpm run typecheck`: Run TypeScript type checking
104+
- `pnpm run format`: Format code with Prettier
105+
- `pnpm run e2e`: Run end-to-end tests with Playwright
106+
107+
Some convenience scripts for shortcuts:
108+
109+
- `pnpm run clean`: Execute a clean install of package dependencies
110+
- `pnpm run client <script>`: Run a script within the client package only
111+
- `pnpm run server <script>`: Run a script within the server package only
112+
113+
## Debug
114+
115+
### Server starts on wrong port locally
8116

9117
Sometimes the server fails to shutdown, leaving an instance listening to port 8787. The next time you run `pnpm run server dev`, it will start a new instance and listen to a random port. Running `killall workerd` does not seem to fix it. Instead, get any `workerd` process ID listening to port 8787 (there may be several) and kill it. On macOS:
10118

11119
```sh
12120
lsof -i :8787
13121
kill -9 <pid>
14122
```
123+
124+
## References
125+
126+
- [React](https://react.dev) SPA for the client-side application
127+
- [Tailwind CSS](https://tailwindcss.com/) for styling
128+
- [Tanstack Query](https://tanstack.com/query/latest) for client http request state management
129+
- [Hono](https://hono.dev/) server-side api framework
130+
- [Prettier](https://prettier.io/) for code formatting
131+
- [ESLint](https://eslint.org/) for linting
132+
- [Vitest](https://vitest.dev/) for unit testing
133+
- [Playwright](https://playwright.dev/) for end-to-end testing
134+
- [TypeScript](https://www.typescriptlang.org/) for type checking
135+
- [Cloudflare](https://cloudflare.com) Pages for hosting the client, Worker with KV storage for hosting the server
136+
- [GitHub](https://github.com) workflows for CI and staging deployment
137+
- [Sentry](https://sentry.io/) integration for client-side error tracking
138+
- [pnpm](https://pnpm.io) for performant monorepo package management

client/README.md

Lines changed: 0 additions & 1 deletion
This file was deleted.

client/package.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
{
22
"name": "@simon/client",
3-
"private": true,
43
"version": "0.0.0",
54
"type": "module",
65
"scripts": {

client/src/server-types.d.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,11 @@ declare module "high-score" {
4343
$get: {
4444
input: {};
4545
output: {
46-
highScore: any;
46+
highScore: {
47+
name: string;
48+
score: number;
49+
timestamp: number;
50+
};
4751
};
4852
outputFormat: "json";
4953
status: 200;
@@ -80,7 +84,11 @@ declare module "index" {
8084
$get: {
8185
input: {};
8286
output: {
83-
highScore: any;
87+
highScore: {
88+
name: string;
89+
score: number;
90+
timestamp: number;
91+
};
8492
};
8593
outputFormat: "json";
8694
status: 200;

docs/simon-arch.png

-10.7 KB
Loading

package.json

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
{
22
"name": "simon",
33
"type": "module",
4-
"private": true,
54
"version": "0.0.0",
65
"description": "Simon: The classic memory game",
76
"main": "index.js",
@@ -12,6 +11,7 @@
1211
"build": "pnpm -r build",
1312
"ci": "pnpm lint && pnpm typecheck && pnpm test",
1413
"clean": "rm -rf node_modules && pnpm -r exec rm -rf node_modules && pnpm i",
14+
"dev": "pnpm -r dev",
1515
"e2e": "pnpm exec playwright test",
1616
"e2e:ui": "pnpm exec playwright test --ui",
1717
"format": "pnpm exec prettier --write **/*.{js,ts,tsx,json,css,md}",
@@ -21,22 +21,33 @@
2121
"test:watch": "pnpm -r test:watch",
2222
"typecheck": "pnpm -r typecheck"
2323
},
24-
"keywords": [],
25-
"author": "",
24+
"keywords": [
25+
"simon",
26+
"memory game",
27+
"state machine",
28+
"fsm",
29+
"react",
30+
"game design"
31+
],
32+
"author": "@codenickycode",
2633
"license": "ISC",
34+
"repository": {
35+
"type": "git",
36+
"url": "https://github.com/codenickycode/simon.git"
37+
},
2738
"dependencies": {
2839
"hono": "^4.5.8"
2940
},
3041
"devDependencies": {
3142
"@playwright/test": "^1.47.0",
3243
"@types/eslint": "^9.6.0",
33-
"@types/node": "^20.14.12",
44+
"@types/node": "^22.7.6",
3445
"@typescript-eslint/eslint-plugin": "^7.14.1",
3546
"@typescript-eslint/parser": "^7.14.1",
3647
"eslint": "^8.57.0",
3748
"prettier": "^3.3.3",
3849
"typescript": "^5.5.3",
39-
"wrangler": "^3.73.0"
50+
"wrangler": "^3.80.5"
4051
},
4152
"engines": {
4253
"node": ">=22.9.0",

0 commit comments

Comments
 (0)