Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
91 changes: 25 additions & 66 deletions bin/check-accretive
Original file line number Diff line number Diff line change
Expand Up @@ -31,68 +31,26 @@ FACTOR = os.environ.get("FACTOR", "factor")
TOOLS = os.path.join(REPO, "lib/exercism-tools")
CONCEPT = os.path.join(REPO, "exercises/concept")

# Intended task ownership: word -> task number it is implemented in, per the
# instructions. Only body-bearing words are listed (these are what we re-stub);
# tuples/constants/symbols are left intact so Mode-B runs always compile and we
# isolate runtime ordering. Keep this in sync with the exercises' instructions.
MAPS = {
"annalyns-infiltration": {"can-do-fast-attack":1,"can-spy":2,"can-signal-prisoner":3,"can-free-prisoner":4},
"backyard-birdcount": {"today":1,"increment-day-count":2,"has-day-without-birds?":3,"total":4,"busy-days":5},
"backyard-birdwatcher": {"today":1,"increment-todays-count":2,"has-day-without-birds?":3,"count-for-first-days":4,"busy-days":5,"pad-missing-days":6},
"belgian-boxcars": {"couple":1,"peek-couplings":2,"split-at-junctions":3,"coalesce-cargo":4},
"boardwalk-games": {"roll-die":1,"pick-prize":2,"shuffle-deck":3,"deal-hand":4,"play-seeded":5},
"bosuns-briefing": {"greeting":1,"crew-line":2,"closing":3,"roster":4,"briefing":5},
"boutique-bookkeeping": {"sort-by-price":1,"with-missing-price":2,"expensive-items":3,"cheapest-item":4,"total-price":5,"format-price-tag":6},
"bunting-bonanza": {"alphabet-bunting":1,"counting-bunting":2,"stripe-bunting":3,"marker-bunting":4,"valley-bunting":5},
"cargo-shuffle": {"swap-crates":1,"clear-spill":2,"peek-under":3,"tidy-deck":4},
"cars-assemble": {"production-status":1,"success-rate":2,"production-rate-per-hour":3,"working-items-per-minute":4},
"channel-chatter": {"hear-out":1,"count-messages":2,"echo-back":3,"broadcast":4,"capture":5},
"character-study": {"compare-chars":1,"size-of-char":2,"change-size-of-char":3,"type-of-char":4},
"coordinate-choreography": {"translate-2d":1,"scale-2d":2,"compose-transformations":3,"apply-transformation":4,"transform-points":5},
"currency-conversion": {"exchange-money":1,"get-change":2,"value-of-bills":3,"number-of-bills":4,"leftover-of-bills":5,"exchangeable-value":6,"safe-change":7,"cap-spend":8},
"dragons-descendants": {"<dragon>":1,"<fire-dragon>":2,"<ice-dragon>":3,"<volcano-dragon>":4,"age-dragon":5},
"factory-failsafe": {"check-humidity":2,"check-temperature":3,"monitor":4,"monitor-batch":5},
"ferry-schedule": {"make-date":1,"weekday-name":2,"leap?":3,"month-length":4,"add-days":5},
"garden-gathering": {"open-garden":1,"list-registrations":1,"register":2,"release":3,"get-registration":4,"find-by-name":5},
"high-school-sweetheart": {"cleanupname":1,"firstletter":2,"initial":3,"couple":4},
"joiners-journey": {"with-kerf":1,"kerf-and-finish":2,"cut-card":3,"per-piece":4,"compare-bolts":5},
"lap-leaderboard": {"assign-bibs":1,"lane-labels":2,"tag-racers":3,"record-finishes":4,"lap-bells":5},
"lasagna": {"preparation-time":2,"remaining-time":3,"total-working-time":4},
"lasagna-luminary": {"cooking-status":1,"preparation-time":2,"quantities":3,"add-secret-ingredient":4,"scale-recipe":5},
"ledger-lookout": {"valid-amount?":1,"dollar-amounts":2,"percentages":3,"flagged?":4},
"librarians-ledger": {"protected-balance":1,"running-balance":2,"least-balance-so-far":3,"halve-until":4},
"lighthouse-logbook": {"empty-log":1,"sight":2,"seen?":3,"forget-sighting":4,"unique-count":5,"reachable":6},
"log-levels": {"message":1,"log-level":2,"reformat":3},
"mixed-juices": {"time-to-mix-juice":1,"wedges-from-lime":2,"limes-to-cut":2,"order-times":3,"remaining-orders":4},
"mixtape-maker": {"count-combinations":1,"count-permutations":2,"list-combinations":3,"list-permutations":4,"combinations-summing-to":5},
"mosaic-making": {"tile-strip":1,"row-of-three":2,"combine-rows":3,"mirror-row":4,"tile-position":5,"has-colour?":6},
"mosaic-mischief": {"fresh-mosaic":1,"place-tile":2,"chip-tile":3,"recolour-tile":4,"snapshot-mosaic":5,"stash-tile":6,"return-tile":7},
"passphrase-patrol": {"valid-badge?":1,"badge-codes":2,"digit-count":3,"redact":4},
"pirates-path": {"tide-queue":1,"coves-reachable":2,"hop-count":3,"gold-count":4,"treasure-route":5},
"poetry-club": {"new-club":1,"collaborate":2,"circle-of":3,"same-circle?":4},
"pursers-pantry": {"create-inventory":1,"add-items":2,"decrement-items":3,"remove-item":4,"list-inventory":5},
"quayside-crew": {"weigh-crate":1,"weigh-all":2,"<crane>":3,"hoist-crate":4,"crane-tonnage":5,"load-cargo":6},
"role-playing-game": {"introduce":2,"revive":3,"take-damage":4},
"rpn-calculator": {"add-op":1,"multiply-op":2,"apply-op":3,"evaluate":4,"evaluate-named":5,"divide-op":6},
"secrets": {"shift-back":1,"set-bits":2,"flip-bits":3,"clear-bits":4},
"signalers-satchel": {"quote-value":1,"flag-body":2,"split-flag":3,"triangulate":4,"triangle-stats":5},
"telegraphers-tape": {"<tape>":1,"stream-read1":3,"stream-element-type":4,"dispose*":5},
"tellers-triage": {"new-queue":1,"join-queue":2,"next-name":3,"serve-all":4},
"valentines-day": {"rate-restaurant":1,"rate-movie":2,"rate-walk":3,"rate-activity":4,"approval-counts":5},
"vltava-weather-watch": {"read-readings":1,"latest-reading":2,"log-text":3,"record-reading":4,"rewrite-log":5},
}

# Exercises whose Mode-B prefix run we cannot synthesize automatically (MACRO:
# bodies cannot be re-stubbed with a throw, and exercises without TASK: markers
# have no prefixes). Mode A is still checked for them.
MODEB_SKIP = {"signal-stencils", "bering-bearings", "boatswains-bilge"}

# The shipped stub does not make the test file compile, but the missing
# definition is created in task 1, so the exercise is still accretive by the
# implement-first-K-tasks rule (a student who does task 1 sees task 1 pass).
ACCRETIVE_VIA_TASK1 = {"lasagna", "role-playing-game", "factory-failsafe",
"dragons-descendants", "bering-bearings",
"telegraphers-tape"}
# Per-exercise data lives in each exercise's .meta/ownership.json:
#
# "tasks": word -> task number it is implemented in, per the instructions.
# Only body-bearing words are listed (these are what we re-stub);
# tuples/constants/symbols are left intact so Mode-B runs always compile and
# we isolate runtime ordering. Keep this in sync with the instructions.
# "manual-ordering-check": true when the Mode-B prefix run cannot be
# synthesized automatically (MACRO: bodies cannot be re-stubbed with a
# throw, exercises without TASK: markers have no prefixes); checked by hand
# instead. Mode A is still checked.
# "requires-task1-to-compile": true when the shipped stub does not make the
# test file compile, but the missing definition is created in task 1, so the
# exercise is still accretive by the implement-first-K-tasks rule (a student
# who does task 1 sees task 1 pass).
def ownership(slug):
path = os.path.join(CONCEPT, slug, ".meta/ownership.json")
if not os.path.exists(path):
return {}
with open(path) as f:
return json.load(f)

DEF_STARTERS = {":","::","M:","M::","TYPED:","TYPED::","MEMO:","MEMO::"}

Expand Down Expand Up @@ -210,7 +168,7 @@ def modeb_violations(slug):
"""Return dict K -> list of early task numbers whose tests fail, or {} if clean."""
src = os.path.join(CONCEPT, slug)
cfg = json.load(open(os.path.join(src, ".meta/config.json")))
wmap = MAPS[slug]
wmap = ownership(slug)["tasks"]
N = max(wmap.values())
sols, exes = cfg["files"]["solution"], cfg["files"].get("exemplar") or cfg["files"]["example"]
test_base = os.path.basename(cfg["files"]["test"][0])
Expand Down Expand Up @@ -242,6 +200,7 @@ def main():
for slug in slugs:
if not os.path.isdir(os.path.join(CONCEPT, slug)):
continue
own = ownership(slug)
compiles = stub_compiles(slug)
notes = []
verdict = "ACCRETIVE"
Expand All @@ -252,15 +211,15 @@ def main():
verdict = "STRUCTURAL"
notes.append("tests are not grouped by TASK: marker, so no prefix of tasks can pass on its own")
elif not compiles:
if slug in ACCRETIVE_VIA_TASK1:
if own.get("requires-task1-to-compile"):
notes.append("stub omits a definition that task 1 creates; accretive by the implement-first-K rule")
else:
verdict = "MODE-A"
notes.append("stub does not pre-declare everything the test file needs (a "
"tuple/slot/constant/symbol/word is introduced by a task later than task 1)")
if slug in MODEB_SKIP:
if own.get("manual-ordering-check"):
notes.append("Mode-B prefix run skipped (macros / no TASK: markers); checked by hand")
elif slug in MAPS:
elif own.get("tasks"):
bad = modeb_violations(slug)
if bad:
verdict = "MODE-A+B" if verdict == "MODE-A" else "MODE-B"
Expand Down
8 changes: 8 additions & 0 deletions exercises/concept/annalyns-infiltration/.meta/ownership.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"tasks": {
"can-do-fast-attack": 1,
"can-spy": 2,
"can-signal-prisoner": 3,
"can-free-prisoner": 4
}
}
9 changes: 9 additions & 0 deletions exercises/concept/backyard-birdcount/.meta/ownership.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"tasks": {
"today": 1,
"increment-day-count": 2,
"has-day-without-birds?": 3,
"total": 4,
"busy-days": 5
}
}
10 changes: 10 additions & 0 deletions exercises/concept/backyard-birdwatcher/.meta/ownership.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"tasks": {
"today": 1,
"increment-todays-count": 2,
"has-day-without-birds?": 3,
"count-for-first-days": 4,
"busy-days": 5,
"pad-missing-days": 6
}
}
8 changes: 8 additions & 0 deletions exercises/concept/belgian-boxcars/.meta/ownership.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"tasks": {
"couple": 1,
"peek-couplings": 2,
"split-at-junctions": 3,
"coalesce-cargo": 4
}
}
5 changes: 5 additions & 0 deletions exercises/concept/bering-bearings/.meta/ownership.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"tasks": {},
"manual-ordering-check": true,
"requires-task1-to-compile": true
}
9 changes: 9 additions & 0 deletions exercises/concept/boardwalk-games/.meta/ownership.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"tasks": {
"roll-die": 1,
"pick-prize": 2,
"shuffle-deck": 3,
"deal-hand": 4,
"play-seeded": 5
}
}
4 changes: 4 additions & 0 deletions exercises/concept/boatswains-bilge/.meta/ownership.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"tasks": {},
"manual-ordering-check": true
}
9 changes: 9 additions & 0 deletions exercises/concept/bosuns-briefing/.meta/ownership.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"tasks": {
"greeting": 1,
"crew-line": 2,
"closing": 3,
"roster": 4,
"briefing": 5
}
}
10 changes: 10 additions & 0 deletions exercises/concept/boutique-bookkeeping/.meta/ownership.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"tasks": {
"sort-by-price": 1,
"with-missing-price": 2,
"expensive-items": 3,
"cheapest-item": 4,
"total-price": 5,
"format-price-tag": 6
}
}
9 changes: 9 additions & 0 deletions exercises/concept/bunting-bonanza/.meta/ownership.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"tasks": {
"alphabet-bunting": 1,
"counting-bunting": 2,
"stripe-bunting": 3,
"marker-bunting": 4,
"valley-bunting": 5
}
}
8 changes: 8 additions & 0 deletions exercises/concept/cargo-shuffle/.meta/ownership.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"tasks": {
"swap-crates": 1,
"clear-spill": 2,
"peek-under": 3,
"tidy-deck": 4
}
}
8 changes: 8 additions & 0 deletions exercises/concept/cars-assemble/.meta/ownership.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"tasks": {
"production-status": 1,
"success-rate": 2,
"production-rate-per-hour": 3,
"working-items-per-minute": 4
}
}
9 changes: 9 additions & 0 deletions exercises/concept/channel-chatter/.meta/ownership.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"tasks": {
"hear-out": 1,
"count-messages": 2,
"echo-back": 3,
"broadcast": 4,
"capture": 5
}
}
8 changes: 8 additions & 0 deletions exercises/concept/character-study/.meta/ownership.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"tasks": {
"compare-chars": 1,
"size-of-char": 2,
"change-size-of-char": 3,
"type-of-char": 4
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"tasks": {
"translate-2d": 1,
"scale-2d": 2,
"compose-transformations": 3,
"apply-transformation": 4,
"transform-points": 5
}
}
12 changes: 12 additions & 0 deletions exercises/concept/currency-conversion/.meta/ownership.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"tasks": {
"exchange-money": 1,
"get-change": 2,
"value-of-bills": 3,
"number-of-bills": 4,
"leftover-of-bills": 5,
"exchangeable-value": 6,
"safe-change": 7,
"cap-spend": 8
}
}
10 changes: 10 additions & 0 deletions exercises/concept/dragons-descendants/.meta/ownership.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"tasks": {
"<dragon>": 1,
"<fire-dragon>": 2,
"<ice-dragon>": 3,
"<volcano-dragon>": 4,
"age-dragon": 5
},
"requires-task1-to-compile": true
}
9 changes: 9 additions & 0 deletions exercises/concept/factory-failsafe/.meta/ownership.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"tasks": {
"check-humidity": 2,
"check-temperature": 3,
"monitor": 4,
"monitor-batch": 5
},
"requires-task1-to-compile": true
}
9 changes: 9 additions & 0 deletions exercises/concept/ferry-schedule/.meta/ownership.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"tasks": {
"make-date": 1,
"weekday-name": 2,
"leap?": 3,
"month-length": 4,
"add-days": 5
}
}
10 changes: 10 additions & 0 deletions exercises/concept/garden-gathering/.meta/ownership.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"tasks": {
"open-garden": 1,
"list-registrations": 1,
"register": 2,
"release": 3,
"get-registration": 4,
"find-by-name": 5
}
}
8 changes: 8 additions & 0 deletions exercises/concept/high-school-sweetheart/.meta/ownership.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"tasks": {
"cleanupname": 1,
"firstletter": 2,
"initial": 3,
"couple": 4
}
}
9 changes: 9 additions & 0 deletions exercises/concept/joiners-journey/.meta/ownership.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"tasks": {
"with-kerf": 1,
"kerf-and-finish": 2,
"cut-card": 3,
"per-piece": 4,
"compare-bolts": 5
}
}
9 changes: 9 additions & 0 deletions exercises/concept/lap-leaderboard/.meta/ownership.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"tasks": {
"assign-bibs": 1,
"lane-labels": 2,
"tag-racers": 3,
"record-finishes": 4,
"lap-bells": 5
}
}
9 changes: 9 additions & 0 deletions exercises/concept/lasagna-luminary/.meta/ownership.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"tasks": {
"cooking-status": 1,
"preparation-time": 2,
"quantities": 3,
"add-secret-ingredient": 4,
"scale-recipe": 5
}
}
8 changes: 8 additions & 0 deletions exercises/concept/lasagna/.meta/ownership.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"tasks": {
"preparation-time": 2,
"remaining-time": 3,
"total-working-time": 4
},
"requires-task1-to-compile": true
}
8 changes: 8 additions & 0 deletions exercises/concept/ledger-lookout/.meta/ownership.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"tasks": {
"valid-amount?": 1,
"dollar-amounts": 2,
"percentages": 3,
"flagged?": 4
}
}
8 changes: 8 additions & 0 deletions exercises/concept/librarians-ledger/.meta/ownership.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"tasks": {
"protected-balance": 1,
"running-balance": 2,
"least-balance-so-far": 3,
"halve-until": 4
}
}
10 changes: 10 additions & 0 deletions exercises/concept/lighthouse-logbook/.meta/ownership.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"tasks": {
"empty-log": 1,
"sight": 2,
"seen?": 3,
"forget-sighting": 4,
"unique-count": 5,
"reachable": 6
}
}
Loading
Loading