Add random pattern selection (-r / --pattern random)
Pick a different movement pattern every time a sweep is triggered, so the motion varies across the day instead of repeating one shape. - strategies.ts: add the `random` sentinel, `SELECTABLE_PATTERN_NAMES`, `isSelectablePattern`, and `createRandomPicker`. `random` is deliberately NOT a registry entry: it has no path of its own, so `STRATEGIES` stays a total lookup and `PATTERN_NAMES` keeps listing only real generators. The picker is a closure over `last`, giving a uniform draw that never returns the same pattern twice in a row. Building CANONICAL_PATTERNS from the selectable list makes both validation boundaries accept `random` (and loose spellings) for free, and extends the normalization-collision assertion to cover the sentinel. - cli.ts: add `-r`/`--random` plus an exported `selectPattern` holding the conflict rule. `-r` is sugar for `--pattern random`, so the two agreeing is a no-op while `-r -p arc` is rejected as contradictory. The flag folds into `pattern`, so ConfigOverrides, resolveConfig, and move.ts are untouched. `parseCliArgs` now takes its argv as an optional parameter so the flag surface is testable without process.argv. - keeper.ts: resolve `random` via the picker once per trigger, before the loop-mode branch, so a pick holds for a whole loop run rather than changing mid-run. runKeeper builds one picker for the process, so the no-repeat memory spans sweeps minutes apart. Because the pick is a real strategy, --verbose logs the concrete pattern name and a pick with an infinite loopPath still bounces edge-to-edge under --loop. - config.ts / configFile.ts: accept the sentinel where a pattern is valid, and quote the selectable list in errors. No `random` boolean config key — the file spells it "pattern": "random". executor.ts and move.ts needed no changes. Tests: new tests/cli.test.ts (the file had no coverage before) covering the flag surface and the conflict rule; picker tests pinning the no-repeat and full-registry-coverage properties; keeper tests pinning once-per-trigger and once-per-loop-run.
This commit is contained in:
@@ -5,6 +5,35 @@ All notable changes to `move` are documented here.
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
### Added
|
||||
- Random pattern selection: `-r` / `--random`, and `random` as a value for
|
||||
`--pattern` and the `pattern` config key. Every time a sweep is triggered,
|
||||
a different movement pattern is chosen, so the motion varies across the day
|
||||
instead of repeating one shape. Two rules keep it predictable: the same
|
||||
pattern is never chosen twice in a row, and the pick happens once per
|
||||
trigger — in loop mode it holds for the whole loop run rather than changing
|
||||
mid-run. The pick is a real strategy, so `--verbose` logs the concrete
|
||||
pattern name and a pick with an infinite loop path (`line`, `diagonal`)
|
||||
still bounces edge-to-edge under `--loop`.
|
||||
|
||||
`-r` is defined as sugar for `--pattern random`, so passing both is
|
||||
rejected (exit `2`) unless they agree: `move -r -p arc` is an error, while
|
||||
`move -r -p random` is a no-op. There is no `random` boolean config key —
|
||||
the file spells it `"pattern": "random"`.
|
||||
|
||||
`random` is deliberately not a registry entry: it has no path of its own,
|
||||
and the keeper resolves it to a real strategy per sweep. `PATTERN_NAMES`
|
||||
therefore still lists only real generators, with the new
|
||||
`SELECTABLE_PATTERN_NAMES` covering what a user may select.
|
||||
|
||||
### Changed
|
||||
- `parseCliArgs` now takes its argument list as an optional parameter
|
||||
(defaulting to the real command line), so the flag surface is unit-testable
|
||||
without touching `process.argv`. Adds `tests/cli.test.ts`, which previously
|
||||
had no coverage.
|
||||
|
||||
## [1.4.0] - 2026-08-17
|
||||
|
||||
### Added
|
||||
|
||||
Reference in New Issue
Block a user