Level designer
@gameforge/level-designer
Design tile-grid game levels as plain JSON and check them with a validator: equal-width rows, known tiles, one start, an exit you can actually reach (keys and doors included), reachable coins, and a closed border. Writes a JSON report you can keep in CI.
- Activity
- New: be the first to try it
- Installs
- 0 installs
- Likes
- 0 likes
- Clones
- 0 clones
- 1{
- 2 "name": "level-designer",
- 3 "owner": "gameforge",
- 4 "version": "1.0.0",
- 5 "type": "skill",
- 6 "description": "Design tile-grid game levels as plain JSON and check them with a validator: equal-width rows, known tiles, one start, an exit you can actually reach (keys and doors included), reachable coins, and a closed border. Writes a JSON report you can keep in CI.",
- 7 "category": "other",
- 8 "license": "MIT",
- 9 "compat": [
- 10 "claude-code"
- 11 ],
- 12 "permissions": {
- 13 "shell": [
- 14 "node scripts/validate-levels.mjs"
- 15 ],
- 16 "files": {
- 17 "read": [
- 18 "./levels/**"
- 19 ],
- 20 "write": [
- 21 "./levels/**",
- 22 "./out/**"
- 23 ]
- 24 }
- 25 },
- 26 "tests": {
- 27 "cases": [
- 28 {
- 29 "name": "accepts levels with a reachable exit, including behind a key and door",
- 30 "inputs": {
- 31 "levels/01.json": "{\"name\":\"1-1\",\"tiles\":[\"#######\",\"#S.C..#\",\"#.###.#\",\"#....E#\",\"#######\"]}\n",
- 32 "levels/02.json": "{\"name\":\"1-2\",\"tiles\":[\"#######\",\"#S..#E#\",\"#.#.#D#\",\"#K#...#\",\"#######\"]}\n"
- 33 },
- 34 "checks": [
- 35 {
- 36 "type": "command_exits_zero",
- 37 "command": "node scripts/validate-levels.mjs levels --report out/level-report.json"
- 38 },
- 39 {
- 40 "type": "output_matches_schema",
- 41 "path": "out/level-report.json",
- 42 "schema": {
- 43 "type": "object",
- 44 "required": [
- 45 "summary",
- 46 "levels"
- 47 ],
- 48 "properties": {
- 49 "summary": {
- 50 "type": "object",
- 51 "properties": {
- 52 "levels": {
- 53 "const": 2
- 54 },
- 55 "valid": {
- 56 "const": 2
- 57 },
- 58 "invalid": {
- 59 "const": 0
- 60 },
- 61 "warnings": {
- 62 "const": 0
- 63 }
- 64 }
- 65 }
- 66 }
- 67 }
- 68 }
- 69 ]
- 70 },
- 71 {
- 72 "name": "reports an unreachable exit, an uneven row, and a door with no key",
- 73 "inputs": {
- 74 "levels/walled.json": "{\"name\":\"walled\",\"tiles\":[\"#######\",\"#S.#.E#\",\"#..#..#\",\"#######\"]}\n",
- 75 "levels/ragged.json": "{\"name\":\"ragged\",\"tiles\":[\"#####\",\"#S.E#\",\"####\"]}\n",
- 76 "levels/locked.json": "{\"name\":\"locked\",\"tiles\":[\"######\",\"#S.DE#\",\"######\"]}\n"
- 77 },
- 78 "checks": [
- 79 {
- 80 "type": "command_exits_zero",
- 81 "command": "node scripts/validate-levels.mjs levels --report out/level-report.json --exit-zero"
- 82 },
- 83 {
- 84 "type": "output_matches_schema",
- 85 "path": "out/level-report.json",
- 86 "schema": {
- 87 "type": "object",
- 88 "properties": {
- 89 "summary": {
- 90 "type": "object",
- 91 "properties": {
- 92 "levels": {
- 93 "const": 3
- 94 },
- 95 "valid": {
- 96 "const": 0
- 97 },
- 98 "invalid": {
- 99 "const": 3
- 100 }
- 101 }
- 102 }
- 103 }
- 104 }
- 105 },
- 106 {
- 107 "type": "file_contains",
- 108 "path": "out/level-report.json",
- 109 "text": "no exit is reachable from the start"
- 110 },
- 111 {
- 112 "type": "file_contains",
- 113 "path": "out/level-report.json",
- 114 "text": "row 2 is 4 wide, expected 5"
- 115 },
- 116 {
- 117 "type": "file_contains",
- 118 "path": "out/level-report.json",
- 119 "text": "has a door (D) but no key (K)"
- 120 }
- 121 ]
- 122 },
- 123 {
- 124 "name": "flags an unreachable coin as a warning, not an error",
- 125 "inputs": {
- 126 "levels/coin.json": "{\"name\":\"coin\",\"tiles\":[\"########\",\"#S..E#C#\",\"########\"]}\n"
- 127 },
- 128 "checks": [
- 129 {
- 130 "type": "command_exits_zero",
- 131 "command": "node scripts/validate-levels.mjs levels/coin.json --report out/level-report.json"
- 132 },
- 133 {
- 134 "type": "file_contains",
- 135 "path": "out/level-report.json",
- 136 "text": "coin at row 1, column 6 cannot be reached"
- 137 }
- 138 ]
- 139 }
- 140 ]
- 141 }
- 142}
