Faithful day loop - weather report, cost of lemonade rising over the summer, glasses/signs/price decisions, thunderstorms, heat waves and street crews - with the $$ LEMONSVILLE DAILY FINANCIAL REPORT $$ laid out the way it printed. The simulation is kept out of React so a season can be run headlessly: a careful player compounds $2.00 into roughly $22 over twelve days, while an all-in player goes broke about seven times in ten. Weather scenes are pixel rectangles in SVG and the music is a small chiptune engine on the Web Audio API, so there are no binary assets. The screen is locked to a 4:3 tube and never scrolls.
78 lines
3.1 KiB
Markdown
78 lines
3.1 KiB
Markdown
# Lemonade Stand
|
||
|
||
A browser recreation of the Atari 8-bit BASIC classic — the one you typed in,
|
||
ran on a TV, and lost two dollars to on a cloudy day.
|
||
|
||
React + TypeScript + Vite. No art assets and no audio files: the weather is
|
||
drawn as pixel rectangles in SVG, and the music is generated by a small
|
||
chiptune engine built on the Web Audio API.
|
||
|
||
## Playing
|
||
|
||
```
|
||
npm install
|
||
npm run dev
|
||
```
|
||
|
||
One to four players share the keyboard, hot-seat style. Each morning you see
|
||
the weather, then decide three things: how many glasses to make, how many
|
||
15-cent signs to put up, and what to charge. Then the town votes with its
|
||
pocket money.
|
||
|
||
- Lemonade costs 2 cents a glass on days 1–2, 4 cents through day 7, and
|
||
5 cents after that.
|
||
- Everything you make is made *today*. Unsold glasses are poured away.
|
||
- A hot, dry day brings a bigger crowd that will pay more. A heat wave is
|
||
better still.
|
||
- A cloudy day can turn into a thunderstorm, and a thunderstorm ruins every
|
||
glass you made.
|
||
- Street crews close the road now and then, and they tend to stay for a
|
||
second day.
|
||
- Signs work, but with sharp diminishing returns — the fourth one barely
|
||
earns its 15 cents.
|
||
|
||
You are broke when you cannot afford a single glass. Otherwise the summer runs
|
||
as long as you like; **RETIRE** closes the books and shows the standings.
|
||
|
||
## How it is put together
|
||
|
||
```
|
||
src/
|
||
game/ pure simulation - no React, no DOM
|
||
constants.ts costs, weather profiles, the demand curve
|
||
engine.ts rolls each day and settles the takings
|
||
reducer.ts the day/turn state machine
|
||
rng.ts seeded mulberry32, so a run can be replayed
|
||
audio/
|
||
synth.ts pulse-wave voices, noise percussion, look-ahead sequencer
|
||
tunes.ts the title, trading and closing themes
|
||
components/ the screens, and the CRT they are drawn on
|
||
```
|
||
|
||
The simulation is deliberately kept out of React: `engine.ts` takes a player, a
|
||
decision and a day and hands back a `DayResult`. That is what makes it possible
|
||
to run a few hundred seasons in a script and check that a careful player grows
|
||
their two dollars while a reckless one goes broke about seven times in ten.
|
||
|
||
### The demand curve
|
||
|
||
The original BASIC listing is not reproduced line for line. `constants.ts`
|
||
holds a reconstruction tuned to behave the way the game plays: cheap lemonade
|
||
sells out, sales fall to nothing once the price stops being a bargain, heat
|
||
lifts both the crowd and the price people will tolerate, and signs help a lot
|
||
at first and hardly at all later.
|
||
|
||
### The television
|
||
|
||
An Atari 800 fed a 4:3 television, and a television has no scrollbar. The tube
|
||
is locked to 4:3 and everything inside it is sized in container units, so the
|
||
picture stays in proportion at any size. If a page ever did come out taller
|
||
than the tube, `Fit` scales it down rather than clipping or scrolling it.
|
||
|
||
## Sound
|
||
|
||
Audio starts on the first click or key press, as browsers require. Two pulse
|
||
voices, a triangle bass and filtered white noise for percussion, driven by a
|
||
scheduler that queues notes 200 ms ahead so the music does not stutter when
|
||
React re-renders. `MUSIC` and `SOUND` toggle independently.
|