A MoonBit toolkit for 2D pixel game map parsing and pathfinding.
moonbit-pixelkit is built for small 2D pixel games, grid-based demos, and algorithm teaching examples. It provides a compact tile map model, ASCII/CSV/Tiled JSON import, walkability queries, Dijkstra reachability, and A* path search.
Showcase
Open the browser showcase:
showcase.html
The page renders the same tactical movement preview as a color grid. Click an open tile to preview a new target, switch between the current-turn route and the full route to the goal, or paste an ASCII map into the built-in map lab to validate it and recalculate the preview.
Run the tactical preview:
moon run examples/tactical_preview
It renders a battlefield, the cells an actor can reach this turn, and the planned path:
git clone https://github.com/zhenghao493-netizen/moonbit-pixelkit.git
cd moonbit-pixelkit
moon check
moon build
moon test
Run the checks:
moon check
moon build
moon test
Run the demo:
moon run cmd/main
moon run examples/ascii_maze
moon run examples/reachable_area
moon run examples/weighted_grid
moon run examples/game_loop_stub
moon run examples/turn_based_movement
moon run examples/tactical_preview
moon run examples/tiled_tactical_preview
moon run examples/parse_diagnostics
See ACCEPTANCE.md for reviewer-oriented verification notes.
See CHANGELOG.md for release notes.
See PERFORMANCE.md for benchmark methodology and a local baseline.
Open showcase.html for a browser-based visual preview.
Example
let source = (
#|S..
#|##.
#|..G
)
let map = parse_ascii_map(source).unwrap()
let path = astar(map, point(0, 0), point(2, 2)).unwrap().unwrap()
println("path length: \{path.length()}")
API Overview
point(x, y) creates a Point.
parse_ascii_map(text, options~) parses #, ., S, and G maps by default.
parse_ascii_map_detailed(text, options~) returns ParseError values with ASCII tile coordinates.
parse_csv_map(text) parses CSV tile ids; 1 is treated as a wall.
parse_csv_map_detailed(text) returns structured CSV map errors using default options.
parse_csv_map_with_options_detailed(text, options) returns structured CSV and option errors.
parse_tiled_json(text, options~) imports an orthogonal Tiled JSON map from named collision and optional terrain layers, including unsigned GIDs with flip flags and uncompressed Base64 data.
tiled_options(...) configures Tiled collision GIDs, terrain layer, and movement-cost mapping.
ParseError::message() converts a structured parser error to concise display text while legacy parser APIs continue returning Result[..., String].
TileMap::in_bounds(point) checks map bounds.
TileMap::tile_at(point) returns a tile when the coordinate is valid.
TileMap::is_walkable(point) returns whether movement is allowed.
TileMap::movement_cost(point) returns the tile movement cost.
TileMap::first_point_with_id(id) returns the first matching tile coordinate.
TileMap::single_point_with_id(id) validates that exactly one matching tile exists.
TileMap::path_cost(path) sums movement cost after the starting cell.
TileMap::path_cost_detailed(path) returns a PathError when a path contains an invalid point.
TileMap::validate_path(path, options~) checks for walkable, step-by-step legal routes and returns a path report.
TileMap::validate_path_detailed(path, options~) returns structured empty-path, point, and illegal-step errors.
TileMap::render_ascii_overlay(reachable=..., path=...) renders movement range and planned paths.
neighbors4(point) returns cardinal neighbors.
neighbors8(point) returns cardinal plus diagonal neighbors.
search_options(allow_diagonal=true) enables diagonal movement while still preventing wall-corner cutting by default; use allow_corner_cutting=true only when that behavior is intended.
moonbit-pixelkit is designed to cover a compact tactical-game loop:
Parse an ASCII, CSV, or Tiled JSON grid.
Find named points such as start and goal.
Compute the cells an actor can reach this turn.
Plan a path to a selected target.
Render a terminal overlay for debugging or examples.
let map = parse_ascii_map(source).unwrap()
let actor = map.single_point_with_id("start").unwrap()
let target = point(4, 1)
let preview = movement_preview(map, actor, target, 5).unwrap()
let reachable = preview.reachable_points()
let path = preview.path().unwrap()
println("cost: \{preview.path_cost().unwrap()}")
println("steps: \{map.validate_path(path).unwrap().steps()}")
println(map.render_ascii_overlay(reachable=reachable, path=path))
Tiled JSON Import
Export an orthogonal map from Tiled using its JSON format with inline tile-layer data. By default, the importer reads a Collision layer and treats gid 1 as a wall. Configure a second terrain layer when individual GIDs carry movement costs:
let options = tiled_options(
terrain_layer=Some("Terrain"),
terrain_costs=[terrain_cost("2", 3), terrain_cost("3", 5)],
)
let map = parse_tiled_json(tiled_json, options~).unwrap()
Run the complete import-to-movement-preview example:
moon run examples/tiled_tactical_preview
This intentionally supports the portable core of Tiled JSON: orthogonal maps, named tilelayer entries, native integer data arrays, and uncompressed Base64 little-endian GID data. Infinite maps, chunked layers, compressed Base64 data, TMX, and image assets remain outside the package boundary. Tiled documents both native arrays and optional Base64 layer data in its JSON map format.
Tiled stores horizontal, vertical, diagonal, and legacy rotation state in the high four bits of an unsigned GID. The importer clears those display flags before matching wall_gids or terrain costs, so configure those options with the base GID such as 1 or 3. This follows Tiled’s Global Tile IDs guidance.
Roadmap
Additional gameplay helpers for turn previews and editor tooling.
More package examples after the next Mooncakes release.
Broader Tiled import support such as chunked layers and encoded data, based on real user demand.
moon check
moon build
moon test
moon run cmd/main
moon run examples/ascii_maze
moon run examples/reachable_area
moon run examples/weighted_grid
moon run examples/game_loop_stub
moon run examples/turn_based_movement
moon run examples/tactical_preview
moon package
Git remotes used during contest development:
git push origin main
git push gitlink main
License
MIT
关于
A MoonBit toolkit for 2D pixel game map parsing and pathfinding.
moonbit-pixelkit
A MoonBit toolkit for 2D pixel game map parsing and pathfinding.
moonbit-pixelkitis built for small 2D pixel games, grid-based demos, and algorithm teaching examples. It provides a compact tile map model, ASCII/CSV/Tiled JSON import, walkability queries, Dijkstra reachability, and A* path search.Showcase
Open the browser showcase:
The page renders the same tactical movement preview as a color grid. Click an open tile to preview a new target, switch between the current-turn route and the full route to the goal, or paste an ASCII map into the built-in map lab to validate it and recalculate the preview.
Run the tactical preview:
It renders a battlefield, the cells an actor can reach this turn, and the planned path:
Status
This project is being developed for the 2026 MoonBit open-source ecosystem contest. The current milestone focuses on a small but usable core package:
moon check,moon build,moon test, and benchmark compilationAcceptance Checklist
The repository currently includes the minimum assets expected for a reusable MoonBit package:
moon.modexamples/moon check,moon build, andmoon testmoonbit-pixelkit-project-proposal.mdACCEPTANCE.mdCHANGELOG.mdshowcase.htmlQuick Start
Install the package in another MoonBit project:
Then import it from your
moon.pkg:Run from this repository:
Run the checks:
Run the demo:
Build the publish archive:
Run the release-mode performance baseline:
See ACCEPTANCE.md for reviewer-oriented verification notes. See CHANGELOG.md for release notes. See PERFORMANCE.md for benchmark methodology and a local baseline. Open
showcase.htmlfor a browser-based visual preview.Example
API Overview
point(x, y)creates aPoint.parse_ascii_map(text, options~)parses#,.,S, andGmaps by default.parse_ascii_map_detailed(text, options~)returnsParseErrorvalues with ASCII tile coordinates.parse_csv_map(text)parses CSV tile ids;1is treated as a wall.parse_csv_map_detailed(text)returns structured CSV map errors using default options.parse_csv_map_with_options_detailed(text, options)returns structured CSV and option errors.parse_tiled_json(text, options~)imports an orthogonal Tiled JSON map from named collision and optional terrain layers, including unsigned GIDs with flip flags and uncompressed Base64 data.parse_tiled_json_detailed(text, options~)returns structured JSON field, orientation, and layer import errors.tiled_options(...)configures Tiled collision GIDs, terrain layer, and movement-cost mapping.ParseError::message()converts a structured parser error to concise display text while legacy parser APIs continue returningResult[..., String].TileMap::in_bounds(point)checks map bounds.TileMap::tile_at(point)returns a tile when the coordinate is valid.TileMap::is_walkable(point)returns whether movement is allowed.TileMap::movement_cost(point)returns the tile movement cost.TileMap::first_point_with_id(id)returns the first matching tile coordinate.TileMap::single_point_with_id(id)validates that exactly one matching tile exists.TileMap::path_cost(path)sums movement cost after the starting cell.TileMap::path_cost_detailed(path)returns aPathErrorwhen a path contains an invalid point.TileMap::validate_path(path, options~)checks for walkable, step-by-step legal routes and returns a path report.TileMap::validate_path_detailed(path, options~)returns structured empty-path, point, and illegal-step errors.TileMap::render_ascii_overlay(reachable=..., path=...)renders movement range and planned paths.neighbors4(point)returns cardinal neighbors.neighbors8(point)returns cardinal plus diagonal neighbors.search_options(allow_diagonal=true)enables diagonal movement while still preventing wall-corner cutting by default; useallow_corner_cutting=trueonly when that behavior is intended.bfs_reachable(map, start, max_cost, options~)returns reachable cells.bfs_reachable_detailed(map, start, max_cost, options~)returns structured start and movement-budget errors.bfs_reachable_with_costs(map, start, max_cost, options~)uses a Dijkstra frontier to return reachable cells with minimum accumulated movement costs.movement_preview(map, start, target, max_cost, options~)returns range, target reachability, target cost, and target path in one call.movement_preview_detailed(...)returns aPathErrorfor invalid starts, targets, or budgets.astar(map, start, goal, options~)returns a path orNone.astar_detailed(map, start, goal, options~)returns structured endpoint errors.Turn-Based Movement Use Case
moonbit-pixelkitis designed to cover a compact tactical-game loop:startandgoal.Tiled JSON Import
Export an orthogonal map from Tiled using its JSON format with inline tile-layer data. By default, the importer reads a
Collisionlayer and treats gid1as a wall. Configure a second terrain layer when individual GIDs carry movement costs:Run the complete import-to-movement-preview example:
This intentionally supports the portable core of Tiled JSON: orthogonal maps, named
tilelayerentries, native integerdataarrays, and uncompressed Base64 little-endian GID data. Infinite maps, chunked layers, compressed Base64 data, TMX, and image assets remain outside the package boundary. Tiled documents both native arrays and optional Base64 layer data in its JSON map format.Tiled stores horizontal, vertical, diagonal, and legacy rotation state in the high four bits of an unsigned GID. The importer clears those display flags before matching
wall_gidsor terrain costs, so configure those options with the base GID such as1or3. This follows Tiled’s Global Tile IDs guidance.Roadmap
Release
Version
0.2.0is published on Mooncakes:https://mooncakes.io/docs/ttxiangshang/moonbit-pixelkit
The
v0.2.0tag matches the current Mooncakes release.Local packaging is verified with:
Recommended release tag:
Development Notes
Useful verification commands:
Git remotes used during contest development:
License
MIT