{"id":21034639,"url":"https://github.com/lpenz/sqrid","last_synced_at":"2025-06-11T11:19:28.225Z","repository":{"id":57668367,"uuid":"397736480","full_name":"lpenz/sqrid","owner":"lpenz","description":"Square coordinates and grid-like arrays, with zero dependencies, in a single file","archived":false,"fork":false,"pushed_at":"2024-12-21T17:10:25.000Z","size":313,"stargazers_count":6,"open_issues_count":0,"forks_count":1,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-04-22T08:21:23.832Z","etag":null,"topics":["grid","rust","rust-crate","rust-library","square"],"latest_commit_sha":null,"homepage":"","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/lpenz.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":"AUTHORS","dei":null,"publiccode":null,"codemeta":null}},"created_at":"2021-08-18T21:14:37.000Z","updated_at":"2025-03-03T20:23:23.000Z","dependencies_parsed_at":"2024-11-20T23:22:31.618Z","dependency_job_id":"3d4bb76f-15dd-427e-8468-f5c1131eba96","html_url":"https://github.com/lpenz/sqrid","commit_stats":{"total_commits":123,"total_committers":1,"mean_commits":123.0,"dds":0.0,"last_synced_commit":"4bc50abdb26041f0f77a4279cc665b46c7c7a236"},"previous_names":[],"tags_count":27,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lpenz%2Fsqrid","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lpenz%2Fsqrid/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lpenz%2Fsqrid/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lpenz%2Fsqrid/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/lpenz","download_url":"https://codeload.github.com/lpenz/sqrid/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254349636,"owners_count":22056386,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["grid","rust","rust-crate","rust-library","square"],"created_at":"2024-11-19T13:07:48.806Z","updated_at":"2025-05-15T13:32:57.756Z","avatar_url":"https://github.com/lpenz.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"[![CI](https://github.com/lpenz/sqrid/actions/workflows/ci.yml/badge.svg)](https://github.com/lpenz/sqrid/actions/workflows/ci.yml)\n[![coveralls](https://coveralls.io/repos/github/lpenz/sqrid/badge.svg?branch=main)](https://coveralls.io/github/lpenz/sqrid?branch=main)\n[![crates.io](https://img.shields.io/crates/v/sqrid)](https://crates.io/crates/sqrid)\n[![doc.rs](https://docs.rs/sqrid/badge.svg)](https://docs.rs/sqrid)\n\n# sqrid\n\n*sqrid* provides square grid coordinates and related operations,\nin a crate with zero dependencies.\n\nIt's easier to explain the features of this crate in terms of the\ntypes it provides:\n- [`Pos`]: position, as absolute coordinates in a grid of fixed\n  size. The dimensions of the grid are const generics type\n  parameters; invalid coordinates can't be created.\n- [`Dir`]: \"movement\", relative coordinates. These are the\n  cardinal (and intercardinal) directions.\n  Addition is implemented in the form of `Pos + Dir = Option\u003cPos\u003e`,\n  which can be `None` if the result is outside the grid.\n- [`Grid`]: a `Pos`-indexed array.\n- [`Gridbool`]: a bitmap-backed `Pos`-indexed grid of booleans.\n- [`Sqrid`]: \"factory\" type that acts as an entry point to the\n  fundamental types below and to algorithms.\n\nWe also have traits that generalize `Grid` and `Gridbool`:\n- [`MapPos`]: trait that maps `Pos` to parameterized items;\n  it's implemented by `Grid`, and some `HashMap`/`BTreeMap` based types.\n- [`SetPos`]: trait that maps each `Pos` to a bool; it's implemented\n  by `Gridbool`, `HashSet\u003cPos\u003e` and `BTreeSet\u003cPos\u003e`.\n\nWe then use these generalization to implement some grid algorithms:\n- [`bf`]: breadth-first iteration and search.\n- [`astar`]: A* search that takes a destination `Pos`.\n- [`ucs`]: uniform-cost search.\n\nAll basic types have the standard `iter`, `iter_mut`, `extend`,\n`as_ref`, and conversion operations that should be expected.\n\n## Fundamental types\n\n### `Pos`: absolute coordinates, position\n\nThe [`Pos`] type represents an absolute position in a square\ngrid. The type itself receives the height and width of the grid as\nconst generic parameter.\n\nWe should usually create a type alias for the grid size we are using:\n\n```rust\nuse sqrid;\n\ntype Pos = sqrid::Pos\u003c6, 7\u003e;\n\nlet pos = Pos::new(3, 3)?;\n```\n\nWe can only generate [`Pos`] instances that are inside the passed\ndimensions.\n\n### `Dir`: relative coordinates, direction, movement\n\nThe [`Dir`] type represents a relative movement of one square. It\ncan only be one of the 8 cardinal and intercardinal directions:\n[`Dir::N`], [`Dir::NE`], [`Dir::E`], [`Dir::SE`], [`Dir::S`],\n[`Dir::SW`], [`Dir::W`], [`Dir::NW`].\n\nIt's a building block for paths, iterating on a [`Pos`] neighbors,\netc. It effectively represents the edges in a graph where the\n[`Pos`] type represents nodes.\n\nAll functions that iterate on `Dir` values accept a boolean const\nargument that specifies whether the intercardinal directions\n(`NE`, `SE`, `SW`, `NW`) should be considered.\n\n### `Grid`: a `Pos`-indexed array\n\nA [`Grid`] is a generic array that can be indexed by a [`Pos`].\n\nWe can create the type from a suitable [`Sqrid`] type by using the\n[`grid_create`] macro. We can then interact with specific lines\nwith [`Grid::line`] and [`Grid::line_mut`], or with the whole\nunderlying array with `as_ref` (see [`std::convert::AsRef`]) and\n`as_mut` (see [`std::convert::AsMut`]).\n\nUsage example:\n\n```rust\ntype Sqrid = sqrid::sqrid_create!(3, 3, false);\ntype Pos = sqrid::pos_create!(Sqrid);\ntype Grid = sqrid::grid_create!(Sqrid, i32);\n\n// The grid create macro above is currently equivalent to:\ntype Grid2 = sqrid::Grid\u003ci32, { Sqrid::WIDTH }, { Sqrid::HEIGHT },\n                              { (Sqrid::WIDTH * Sqrid::HEIGHT) as usize }\u003e;\n\n// We can create grids from iterators via `collect`:\nlet mut gridnums = (0..9).collect::\u003cGrid\u003e();\n\n// Iterate on their members:\nfor i in \u0026gridnums {\n    println!(\"i {}\", i);\n}\n\n// Change the members in a loop:\nfor i in \u0026mut gridnums {\n    *i *= 10;\n}\n\n// Iterate on (coordinate, member) tuples:\nfor (pos, \u0026i) in gridnums.iter_pos() {\n    println!(\"[{}] = {}\", pos, i);\n}\n\n// And we can always use `as_ref` or `as_mut` to interact with the\n// inner array directly. To reverse it, for example, with the\n// [`std::slice::reverse`] function:\ngridnums.as_mut().reverse();\n```\n\n### `Gridbool`: a bitmap-backed `Pos`-indexed grid of booleans\n\nThe [`Gridbool`] is a compact abstraction of a grid of booleans.\n\nThe type itself can be created with [`gridbool_create`] macro.\nIt's optimized for getting and setting values at specific\ncoordinates, but we can also get all `true`/`false` coordinates\nwith suboptimal performance - in this case, the time is\nproportional to the size of the grid and not to the quantity of\n`true`/`false` values.\n\nUsage example:\n\n```rust\ntype Sqrid = sqrid::sqrid_create!(3, 3, false);\ntype Pos = sqrid::pos_create!(Sqrid);\ntype Gridbool = sqrid::gridbool_create!(Sqrid);\n\n// We can create a gridbool from a Pos iterator via `collect`:\nlet mut gb = Pos::iter().filter(|pos| pos.is_corner()).collect::\u003cGridbool\u003e();\n\n// We can also set values from an iterator:\ngb.set_iter_t(Pos::iter().filter(|pos| pos.is_side()));\n\n// Iterate on the true/false values:\nfor b in gb.iter() {\n    println!(\"{}\", b);\n}\n\n// Iterate on the true coordinates:\nfor pos in gb.iter_t() {\n    assert!(pos.is_side());\n}\n\n// Iterate on (coordinate, bool):\nfor (pos, b) in gb.iter_pos() {\n    println!(\"[{}] = {}\", pos, b);\n}\n```\n\n## `Sqrid`: entry point for algorithms\n\nThe [`Pos`] type and some methods on the [`Dir`] type require const\ngeneric arguments that usually don't change inside an application.\nBoth [`Grid`] and [`Gridbool`] also require further arguments that\ncan actually be derived from the width and height of the grid, but\nthat have to be explicitly specified due to some Rust limitations.\n\nTo make the creation of these types easier, we provide the\n[`Sqrid`] type, which acumulates all const generic parameters and\ncan be used to create the other types via macros.\n\nExample usage:\n\n```rust\ntype Sqrid = sqrid::sqrid_create!(4, 4, false);\ntype Pos = sqrid::pos_create!(Sqrid);\ntype Grid = sqrid::grid_create!(Sqrid, i32);\ntype Gridbool = sqrid::gridbool_create!(Sqrid);\n```\n\n## Algorithms\n\n### Breadth-first traversal\n\nThe [`Sqrid::bf_iter`] function instantiates an iterator struct\n([`bf::BfIterator`]) that can be used to iterate coordinates in\nbreadth-first order, from a given origin, using a provided\nfunction to evaluate a given [`Pos`] position + [`Dir`] direction\ninto the next `Pos` position.\n\nExample usage:\n\n```rust\ntype Sqrid = sqrid::sqrid_create!(3, 3, false);\ntype Pos = sqrid::pos_create!(Sqrid);\n\nfor (pos, dir) in Sqrid::bf_iter(sqrid::mov_eval, \u0026Pos::CENTER)\n                .flatten() {\n    println!(\"breadth-first pos {} from {}\", pos, dir);\n}\n```\n\n### Breadth-first search\n\n[`Sqrid::bfs_path`] takes an origin, a movement function and a\ngoal function, and figures out the shortest path to a goal by\nusing a breadth-first iteration.\n\nThe function returns the [`Pos`] that fulfills the goal and a\npath in the form of a `Vec\u003cDir\u003e`.\n\nExample usage:\n\n```rust\ntype Sqrid = sqrid::sqrid_create!(3, 3, false);\ntype Pos = sqrid::pos_create!(Sqrid);\n\n// Generate the grid of \"came from\" directions from bottom-right to\n// top-left:\nif let Ok((goal, path)) = Sqrid::bfs_path(\n                              sqrid::mov_eval, \u0026Pos::TOP_LEFT,\n                              |pos| pos == Pos::BOTTOM_RIGHT) {\n    println!(\"goal: {}, path: {:?}\", goal, path);\n}\n```\n\n### A* search\n\n[`Sqrid::astar_path`] takes a movement function, an origin and a\ndestination, and figures out the shortest path by using A*.\n\nThe function returns path in the form of a `Vec\u003cDir\u003e`.\n\nExample usage:\n\n```rust\ntype Sqrid = sqrid::sqrid_create!(3, 3, false);\ntype Pos = sqrid::pos_create!(Sqrid);\n\n// Generate the grid of \"came from\" directions from bottom-right to\n// top-left:\nif let Ok(path) = Sqrid::astar_path(sqrid::mov_eval, \u0026Pos::TOP_LEFT,\n                                    \u0026Pos::BOTTOM_RIGHT) {\n    println!(\"path: {:?}\", path);\n}\n```\n\n### Uniform-cost search\n\n[`Sqrid::ucs_path`] takes a movement-cost function, an origin and a\ndestination, and figures out the path with the lowest cost by using\nuniform-cost search, which is essentially a variation of\n[Dijkstra](https://en.wikipedia.org/wiki/Dijkstra%27s_algorithm).\n\nThe function returns path in the form of a `Vec\u003cDir\u003e`.\n\nExample usage:\n\n```rust\ntype Sqrid = sqrid::sqrid_create!(3, 3, false);\ntype Pos = sqrid::pos_create!(Sqrid);\n\nfn traverse(position: Pos, direction: sqrid::Dir) -\u003e Option\u003c(Pos, usize)\u003e {\n    let next_position = (position + direction).ok()?;\n    let cost = 1;\n    Some((next_position, cost))\n}\n\n// Generate the grid of \"came from\" directions from bottom-right to\n// top-left:\nif let Ok(path) = Sqrid::ucs_path(traverse, \u0026Pos::TOP_LEFT,\n                                  \u0026Pos::BOTTOM_RIGHT) {\n    println!(\"path: {:?}\", path);\n}\n```\n\n[`std::convert::AsRef`]: https://doc.rust-lang.org/std/convert/trait.AsRef.html\n[`std::convert::AsMut`]: https://doc.rust-lang.org/std/convert/trait.AsMut.html\n[`Pos`]: https://docs.rs/sqrid/latest/sqrid/Pos/struct.Pos.html\n[`Pos::FIRST`]: https://docs.rs/sqrid/latest/sqrid/Pos/struct.Pos.html#associatedconstant.FIRST\n[`Pos::LAST`]: https://docs.rs/sqrid/latest/sqrid/Pos/struct.Pos.html#associatedconstant.LAST\n[`Pos::TOP_LEFT`]: https://docs.rs/sqrid/latest/sqrid/Pos/struct.Pos.html#associatedconstant.TOP_LEFT\n[`Pos::CENTER`]: https://docs.rs/sqrid/latest/sqrid/Pos/struct.Pos.html#associatedconstant.CENTER\n[`Pos::new`]: https://docs.rs/sqrid/latest/sqrid/Pos/struct.Pos.html#method.new\n[`Pos::iter`]: https://docs.rs/sqrid/latest/sqrid/Pos/struct.Pos.html#method.iter\n[`Dir`]: https://docs.rs/sqrid/latest/sqrid/Dir/enum.Dir.html\n[`Dir::iter`]: https://docs.rs/sqrid/latest/sqrid/Dir/enum.Dir.html#method.iter\n[`Dir::N`]: https://docs.rs/sqrid/latest/sqrid/Dir/enum.Dir.html#variant.N\n[`Dir::NE`]: https://docs.rs/sqrid/latest/sqrid/Dir/enum.Dir.html#variant.NE\n[`Dir::E`]: https://docs.rs/sqrid/latest/sqrid/Dir/enum.Dir.html#variant.E\n[`Dir::SE`]: https://docs.rs/sqrid/latest/sqrid/Dir/enum.Dir.html#variant.SE\n[`Dir::S`]: https://docs.rs/sqrid/latest/sqrid/Dir/enum.Dir.html#variant.S\n[`Dir::SW`]: https://docs.rs/sqrid/latest/sqrid/Dir/enum.Dir.html#variant.SW\n[`Dir::W`]: https://docs.rs/sqrid/latest/sqrid/Dir/enum.Dir.html#variant.W\n[`Dir::NW`]: https://docs.rs/sqrid/latest/sqrid/Dir/enum.Dir.html#variant.NW\n[`Grid`]: https://docs.rs/sqrid/latest/sqrid/grid/struct.Grid.html\n[`grid_create`]: https://docs.rs/sqrid/latest/sqrid/macro.grid_create.html\n[`Grid::line`]: https://docs.rs/sqrid/latest/sqrid/grid/struct.Grid.html#method.line\n[`Grid::line_mut`]: https://docs.rs/sqrid/latest/sqrid/grid/struct.Grid.html#method.line_mut\n[`Gridbool`]: https://docs.rs/sqrid/latest/sqrid/gridbool/struct.Gridbool.html\n[`gridbool_create`]: https://docs.rs/sqrid/latest/sqrid/macro.gridbool_create.html\n[`Sqrid`]: https://docs.rs/sqrid/latest/sqrid/struct.Sqrid.html\n[`Sqrid::bf_iter`]: https://docs.rs/sqrid/latest/sqrid/base/struct.Sqrid.html#method.bf_iter\n[`bf::BfIterator`]: https://docs.rs/sqrid/latest/sqrid/struct.BfIterator.html\n[`bf`]: https://docs.rs/sqrid/latest/sqrid/bf\n[`astar`]: https://docs.rs/sqrid/latest/sqrid/astar\n[`ucs`]: https://docs.rs/sqrid/latest/sqrid/ucs\n[`Sqrid::bfs_path`]: https://docs.rs/sqrid/latest/sqrid/base/struct.Sqrid.html#method.bfs_path\n[`Sqrid::astar_path`]: https://docs.rs/sqrid/latest/sqrid/base/struct.Sqrid.html#method.astar_path\n[`Sqrid::ucs_path`]: https://docs.rs/sqrid/latest/sqrid/base/struct.Sqrid.html#method.ucs_path\n[`MapPos`]: https://docs.rs/sqrid/latest/sqrid/mappos/trait.MapPos.html\n[`SetPos`]: https://docs.rs/sqrid/latest/sqrid/setpos/trait.SetPos.html\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flpenz%2Fsqrid","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Flpenz%2Fsqrid","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flpenz%2Fsqrid/lists"}