Skip to content

tilttek/progression

✅ published

Experience that adds up into levels, seasons that start a track again, and streaks counted in each member's own day.

tilttek/progression turns the members' activity, recorded by tilttek/activity, into experience and streaks. Experience is earned by rules — fifty per game run as game master, two hundred at most a day — into an account that is never spent, and a curve says the experience each level starts at: linear, quadratic, exponential, or a table the application writes. A site keeps as many tracks as it needs — player, game master, author —, each going to the member or to the gesture's object, such as a character whose experience follows the rules of its game. A track may start again at each season — every month, quarter or year, or named periods such as a summer event — and keeps what was reached in the seasons past; milestones of experience or level are told once, for the application to reward. A streak counts the days or weeks in a row a member makes a gesture, read in the member's timezone, so that midnight in Montréal does not break a streak in Paris; a grace forgives a few missed days, a frozen day keeps the run alive, and the best run is kept beside the current one. Everything is replayed from the journal when a rule changes, and erasing a member removes what they earned.

It is for an application that rewards what its members do and wants them to see how far they have come — a community site, a game site, a reading or writing platform. tilttek/progression-bundle integrates it with Symfony and keeps the progression in Doctrine DBAL; an application without Symfony takes the library alone and writes its own stores, or uses those in memory.

It is a plain PHP library whose dependencies are tilttek/activity, the journal its rules and streaks read, phil/resource, the opaque reference of a member or an object, and psr/clock. Members are references the application gives, member:44: the library never reads a name, an e-mail or an account.

The design is small: a Track holds a Curve, its ExperienceRules, its SeasonSchedule and its Milestones; ProgressionProjection reads the journal and writes Gains, milestones and streak gestures to three stores; Progression reads standings, seasons past, milestones and streaks, freezes days, sweeps the broken streaks and erases a member; StreakKeeper and StreakCalculator count the runs.

Status

PartState
Curve and Level: linear, quadratic, exponential and table curves, where experience standsImplemented and tested
ExperienceRule, Cap, Period, Gain: experience per gesture, caps per subject and periodImplemented and tested
Track, Tracks, Subject, Milestone: tracks of members or objects, from a configurationImplemented and tested
Season, CalendarSeasons, NamedSeasons: seasons by month, quarter or year, or namedImplemented and tested
StreakRule, StreakRules, StreakUnit, Grace, StreakCalculator, StreakKeeper: streaks of days or weeks, grace, frozen units, the sweepImplemented and tested
TimezoneResolver, FixedTimezone, ContextTimezone: the member's timezoneImplemented and tested
ProgressionProjection: the activity turned into experience and streaks, level changes, milestones, cancellations and rebuildsImplemented and tested
Progression: standings, seasons past, milestones, streaks, freezes, sweep, erasureImplemented and tested
Three stores and their in-memory implementations, with the contracts any store passesImplemented and tested
Exceptions under one ProgressionException interfaceImplemented and tested

Documentation

DocumentWhat it covers
InstallationRequirements and installing.
Tracks and levelsCurves, rules, caps, subjects, standings and the configuration of tracks.
SeasonsCalendar and named seasons, and the seasons past.
MilestonesSteps of experience or level worth a reward, told once.
StreaksDays or weeks in a row, the member's timezone, grace, frozen days and the sweep.
The projectionHow the journal is read, what is told, cancellations and rebuilds.
ErasureWhat erasing a member removes.
Writing a storeThe three store contracts an application's storage implements.
Extension pointsWhat an application gives the library: stores, seasons, timezones, the observer, the clock.
ExceptionsProgressionException and its exceptions, with their messages.
TestingThe library's tests and mutation bar, and how to test code that reads the progression.
ReferenceEvery type of the library, with its role.

Quick start

composer require tilttek/progression
use Tilttek\Progression\Streak\StreakRules;
use Tilttek\Progression\Tracks;

$tracks = Tracks::fromArray([
    'player' => [
        'curve' => ['type' => 'quadratic', 'base' => 50, 'levels' => 60],
        'rules' => ['games' => ['verbs' => ['game.played'], 'experience' => 20, 'cap' => ['amount' => 100, 'period' => 'day']]],
        'milestones' => ['veteran' => ['level' => 10]],
    ],
], 'America/Toronto');
$streaks = StreakRules::fromArray(['daily-play' => ['verbs' => ['game.played'], 'grace' => ['allowed' => 1, 'window' => 7]]]);

// Wire ProgressionProjection to the journal, then read:
$progression->standing($member, 'player')->level->number; // 3
$progression->streak($member, 'daily-play')->current;     // 5

Requirements

PHP 8.5 or later.

Contributing

Contributions are welcome on the PHIL monorepo: read the contribution guide and the code of conduct. Report a vulnerability privately, as the security policy explains, never in a public issue.

License

MIT, © Philippe Gamache, doing business as Le Gnome Archiviste; a Tilttek project. See LICENSE.