This project is a Ruby on Rails application developed for the IKEA Foundation. It is a multi-lingual, interactive game/educational tool where players progress through various stages, making choices or facing "chance" events.
mobility gem for translating content into numerous languages. Supported locales include: en, zh, hr, cs, da, nl, fi, fr, fr-CA, de, hu, it, ja, ko, nb, pl, pt, ro, sr, sk, sl, es, sv, uk.ancestry) for managing pages, stages, and interactive elements.Player model's progress JSONB field.GameController, featuring:
good_answer, bad_answer, or chance).chance, an outcome child is sampled.config/question_scores.json, not in the database. See below.pg_search integration for content discovery.Node: The central content model. Templates are hierarchy-dependent:
startfacts, intro, stage, last_save, resultsgood_answer, bad_answer, chancegood_answer, bad_answerPlayer: Tracks session state, progress (per-stage answer_id / result_id), the
cumulative score, and the scores hash keyed by food_waste, emissions, income.Asset & Attachment: Handles media and its contextual content (body text, styling) associated with nodes.User: Admin authentication and roles.Scores are not stored on nodes. config/question_scores.json holds them, and
GameController#score_entry_for maps a node to its entry by position:
stages[stage_index - 1].answers[answer.position - 1] for a normal answer.chance outcome, the parent chance node's entry is looked up the same way and
the outcome is read from its outcomes[child.position - 1].Each entry has an overall value (feeds player.score and the result band) and an
impact hash of food_waste / emissions / income deltas (feed the per-category
tones in GameHelper). type and early_exit are documentation only — nothing reads them.
Gotcha: because the mapping is positional, the JSON must mirror the node tree exactly.
A chance answer needs a {"type": "chance", "outcomes": [...]} entry at its position; if
it is flattened into sibling entries instead, the outcome lookup silently returns nil and
that branch scores nothing. Verify with a bin/rails runner walk of the tree after editing.
app/controllers/admin/: Admin backend logic.app/controllers/game_controller.rb: Main game loop (stage -> answer -> result -> results).app/controllers/api/: JSON endpoints (see docs/leaderboard_api.md).app/helpers/game_helper.rb: Result bands and per-impact tone thresholds.app/models/concerns/: Shared logic for ancestry, attachments, and tags.config/locales/: YAML translation files.config/question_scores.json: Answer scoring table.