Add several boards per level, CM1/CM2 levels and the CE1 'plage des sons' pilot board (0.6.0)

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
Denis Defolie
2026-10-12 11:05:00 +02:00
parent 0e48b1995e
commit d0380a76af
118 changed files with 2421 additions and 111 deletions
+63
View File
@@ -0,0 +1,63 @@
extends GutTest
## Coherence des « tableaux » (data/curriculum/) avec le reste des donnees.
func test_school_levels_match_the_schemas() -> void:
for schema_name in ["save", "rule", "curriculum", "dictation", "exercise_multiple_choice"]:
var schema := DataLoader.load_schema(schema_name)
var enum_values: Array = []
if schema_name == "save":
enum_values = schema["properties"]["school_level"]["enum"]
else:
enum_values = schema["properties"]["level"]["enum"]
assert_eq(enum_values, SchoolLevels.ALL as Array, "schema %s" % schema_name)
func test_backdrop_enum_matches_available_art() -> void:
var schema := DataLoader.load_schema("curriculum")
var enum_values: Array = schema["properties"]["backdrop"]["enum"]
var kinds: Array = BackdropKinds.kinds()
enum_values.sort()
kinds.sort()
assert_eq(enum_values, kinds)
for kind in BackdropKinds.kinds():
assert_true(
ResourceLoader.exists(BackdropKinds.TEXTURES[kind]), "decor manquant : %s" % kind
)
assert_true(SoundCatalog.MUSIC_IDS.has(BackdropKinds.music_for(kind)))
func test_every_rule_belongs_to_exactly_one_region_of_its_level() -> void:
var owners := {}
for level in SchoolLevels.ALL:
for region in ExerciseEngine.get_regions_for_level(level):
assert_between(
ExerciseEngine.get_regions_for_level(level).size(), 1, 3, "3 tableaux maximum"
)
for rule_id: String in region["rule_ids"]:
assert_false(owners.has(rule_id), "regle dans deux tableaux : %s" % rule_id)
owners[rule_id] = region["region_id"]
var rule := ExerciseEngine.get_rule(rule_id)
assert_false(rule.is_empty(), "regle inconnue : %s" % rule_id)
assert_eq(rule.get("level", ""), level, "niveau de %s" % rule_id)
for mission in ExerciseEngine.get_missions():
assert_true(
owners.has(mission["rule_id"]), "mission hors tableau : %s" % mission["mission_id"]
)
func test_regions_have_a_child_facing_title_and_unique_order() -> void:
for level in SchoolLevels.ALL:
var orders := {}
for region in ExerciseEngine.get_regions_for_level(level):
assert_true(region.has("region_title"), "titre manquant : %s" % region["region_id"])
var order := int(region.get("order", 1))
assert_false(orders.has(order), "numero de tableau en double dans %s" % level)
orders[order] = true
func test_school_levels_helpers() -> void:
assert_eq(SchoolLevels.previous("CE1"), "")
assert_eq(SchoolLevels.previous("CM1"), "CE2")
assert_eq(SchoolLevels.previous("n_importe_quoi"), "")
assert_true(SchoolLevels.rank("CM2") > SchoolLevels.rank("CE2"))
+1
View File
@@ -0,0 +1 @@
uid://domp0yvjne65v
+21 -17
View File
@@ -13,20 +13,24 @@ const MIN_GAP_X := 0.2
const MIN_GAP_Y := 0.3
func test_mission_markers_do_not_overlap_within_a_level() -> void:
for level in ["CE1", "CE2"]:
var missions := ExerciseEngine.get_missions_for_level(level)
assert_false(missions.is_empty(), "aucune mission pour %s" % level)
for i in missions.size():
for j in range(i + 1, missions.size()):
var a: Dictionary = missions[i]["map_position"]
var b: Dictionary = missions[j]["map_position"]
var dx: float = abs(float(a["x"]) - float(b["x"]))
var dy: float = abs(float(a["y"]) - float(b["y"]))
assert_true(
dx >= MIN_GAP_X or dy >= MIN_GAP_Y,
(
"%s et %s sont trop proches (dx=%s, dy=%s)"
% [missions[i]["mission_id"], missions[j]["mission_id"], dx, dy]
),
)
func test_mission_markers_do_not_overlap_within_a_region() -> void:
var checked_regions := 0
for level in SchoolLevels.ALL:
for region in ExerciseEngine.get_regions_for_level(level):
var missions := ExerciseEngine.get_missions_for_region(region["region_id"])
assert_false(missions.is_empty(), "aucune mission pour %s" % region["region_id"])
checked_regions += 1
for i in missions.size():
for j in range(i + 1, missions.size()):
var a: Dictionary = missions[i]["map_position"]
var b: Dictionary = missions[j]["map_position"]
var dx: float = abs(float(a["x"]) - float(b["x"]))
var dy: float = abs(float(a["y"]) - float(b["y"]))
assert_true(
dx >= MIN_GAP_X or dy >= MIN_GAP_Y,
(
"%s et %s sont trop proches (dx=%s, dy=%s)"
% [missions[i]["mission_id"], missions[j]["mission_id"], dx, dy]
),
)
assert_gt(checked_regions, 1)