The Navigator's Grid: Mastering stzGrid in the Softanza Library
stzGrid offers a one-based coordinate system (starting at position [1,1]) for navigating grid spaces without storing data elements itself. Think of it as an intelligent cursor that can move throughout a grid while providing information about its surroundings.
by Mansour Ayouni · in the repository since 2025-05-01 · tutorial · no single Atlas area
The stzGrid class is a powerful navigational framework in the Softanza library, offering elegant solutions for working with two-dimensional grid structures. This article uses visual examples and practical applications to help you fully grasp its capabilities.
Introduction to stzGrid
Let's see what happens when we create a basic 5×5 grid:
load "stzlib.ring" o1 = new stzGrid([5, 5]) # Grid Size ? o1.NumberOfRows() #--> 5 ? o1.NumberOfColumns() #--> 5 ? o1.Size() #--> 25 ? o1.SizeXT() #--> [5, 5] # Current Position ? o1.CurrentPosition() #--> [1, 1]
illustration
When visualized, our new grid looks like this:
1 2 3 4 5 ╭─v─────────╮ 1 > x . . . . │ ← The 'x' shows our cursor at position [1,1] 2 │ . . . . . │ 3 │ . . . . . │ 4 │ . . . . . │ 5 │ . . . . . │ ╰───────────╯
Basic Movement: Navigating Your Grid
Cardinal Directions
stzGrid provides intuitive methods for moving in four directions:
StzGridQ([5, 5]) {
# Starting at [1,1]
MoveDown() # Move down once
MoveDown() # Move down again
? CurrentPosition() #--> [1, 3]
MoveRight() # Move right once
MoveRight() # Move right again
MoveRight() # Move right a third time
? CurrentPosition() #--> [4, 3]
Show()
}illustration
This produces:
1 2 3 4 5 ╭───────v───╮ 1 │ . . . . . │ 2 │ . . . . . │ 3 > . . . x . │ ← We're now at position [4,3] 4 │ . . . . . │ 5 │ . . . . . │ ╰───────────╯
Boundary Intelligence
stzGrid prevents movement beyond grid boundaries:
o1 = new stzGrid([6, 4]) # Move to edge o1.MoveTo(6, 1) # Position [6,1] # Try to move beyond boundary o1.MoveRight() # Won't move ? o1.Position() # Still [6,1]
illustration
Precision Movement
For direct positioning:
o1 = new stzGrid([10, 10]) o1.MoveTo(3, 3) # Jump directly to position [3,3] # Now make a larger movement o1.MoveBy(4, 3) # Move 4 columns right, 3 rows down ? o1.Position() # [7, 6] o1.Show()
illustration
Resulting in:
1 2 3 4 5 6 7 8 9 0 ╭─────────────v───────╮ 1 │ . . . . . . . . . . │ 2 │ . . . . . . . . . . │ 3 │ . . . . . . . . . . │ 4 │ . . . . . . . . . . │ 5 │ . . . . . . . . . . │ 6 > . . . . . . x . . . │ ← Now at position [7,6] 7 │ . . . . . . . . . . │ 8 │ . . . . . . . . . . │ 9 │ . . . . . . . . . . │ 0 │ . . . . . . . . . . │ ╰─────────────────────╯
Understanding Direction-Based Movement
stzGrid maintains a "current direction" concept that enhances movement capabilities:
StzGridQ([4, 4]) {
# Check default direction
? Direction() #--> "forward"
# Change direction
SetDirection(:down)
? Direction() #--> "down"
# Move to center
MoveTo(2, 2)
# Move in current direction (down)
MoveToNextPosition()
? Position() #--> [2, 3]
# Move opposite direction (up)
MoveToPreviousPosition()
? Position() #--> [2, 2]
}illustration
This direction system becomes particularly useful for algorithms that need to traverse paths in specific ways.
Exploring Nearby Positions
One of stzGrid's most powerful features is its ability to analyze neighboring positions:
o1 = new stzGrid([5, 5]) o1.MoveTo(3, 3) #--> Center of grid # Adjacent positions ? o1.NodeAbove() #--> [3, 2] ? o1.NodeBelow() #--> [3, 4] ? o1.NodeToLeft() #--> [2, 3] ? o1.NodeToRight() #--> [4, 3] # Diagonal positions ? o1.NodeAboveLeft() #--> [2, 2] ? o1.NodeAboveRight() #--> [4, 2] ? o1.NodeBelowLeft() #--> [2, 4] ? o1.NodeBelowRight() #--> [4, 4] # All surrounding nodes ? o1.Neighbors() # Or AdjacentNodes() if you prefer # [[2,2], [2,3], [2,4], [3,2], [3,4], [4,2], [4,3], [4,4]] o1.ShowNeighbors()
illustration
This creates a visual representation of the neighborhood:
1 2 3 4 5 ╭─────v─────╮ 1 │ . . . . . │ 2 │ . N N N . │ ← 'N' shows neighboring positions 3 > . N x N . │ ← 'x' is our current position at [3,3] 4 │ . N N N . │ 5 │ . . . . . │ ╰───────────╯
Distance Calculations: Measuring Spatial Relationships
stzGrid supports both Manhattan (grid-based) and Euclidean (straight-line) distance:
StzGridQ([10, 10]) {
MoveTo(3, 3)
# Manhattan Distance (Sum of horizontal and vertical distances)
? DistanceTo(7, 6) #--> 7
# Euclidean Distance (Straight line "as the crow flies")
? EuclideanDistanceTo(7, 6) #--> 5
}illustration
Adding Obstacles: Creating Complex Spaces
Obstacles transform simple grids into complex environments:
o1 = new stzGrid([10, 6]) # Add some obstacles one by one o1.AddObstacle(3, 2) o1.AddObstacle(4, 2) o1.AddObstacle(5, 2) o1.AddObstacle(3, 4) o1.AddObstacle(4, 4) o1.AddObstacle(5, 4) # Show grid with obstacles o1.Show()
illustration
Resulting in:
1 2 3 4 5 6 7 8 9 0 ╭─────────────────────╮ 1 │ x . . . . . . . . . │ 2 │ . . ■ ■ ■ . . . . . │ ← Row of obstacles 3 │ . . . . . . . . . . │ 4 │ . . ■ ■ ■ . . . . . │ ← Another row of obstacles 5 │ . . . . . . . . . . │ 6 │ . . . . . . . . . . │ ╰─────────────────────╯
You can customize obstacle appearance:
o1.SetObstacleChar("B") # Change obstacle character
o1.Show()illustration
Now obstacles appear as:
1 2 3 4 5 6 7 8 9 0 ╭─────────────────────╮ 1 │ x . . . . . . . . . │ 2 │ . . B B B . . . . . │ ← Obstacles now shown as 'B' 3 │ . . . . . . . . . . │ 4 │ . . B B B . . . . . │ 5 │ . . . . . . . . . . │ 6 │ . . . . . . . . . . │ ╰─────────────────────╯
Path Creation and Management
stzGrid excels at creating and managing paths between points:
StzGridQ([8, 6]) {
# Add many obstacles in one command
AddObstacles([ [3, 2], [3, 3], [3, 4], [5, 2], [5, 3], [5, 4] ])
# Create a path manually by specifying the traversed nodes
AddPath([
[1, 1], [2, 1],
[2, 2], [4, 2],
[2, 3], [4, 3],
[2, 4], [4, 4], [6, 2],
[6, 3], [6, 4], [7, 4], [8, 4]
])
# Path information
? PathLength() #--> 13
# Move to end of path and show
MoveTo(8, 4)
Show()
}illustration
The visual result:
1 2 3 4 5 6 7 8 ╭───────────────v─╮ 1 │ ○ ○ · · · · · · │ ← '○' shows path nodes 2 │ · ○ ■ · ■ ○ · · │ ← '■' shows obstacles 3 │ · ○ ■ ○ ■ ○ · · │ 4 > · ○ ■ ○ ■ ○ ○ x │ ← 'x' is our position at [8,4] 5 │ · · · · · · · · │ 6 │ · · · · · · · · │ ╰─────────────────╯
Powerful Pathfinding: Finding Routes Automatically
Instead of manually creating paths, stzGrid offers several pathfinding algorithms:
A* Algorithm - The Shortest Path
Let's construct a grid with some vertical obstacle lines, and then find the shortest path between two nodes while navigating around these obstacles.
StzGridQ([10, 6]) {
# Add obstacles to form a maze
AddObstacles([ [3, 2], [3, 3], [3, 4] ])
AddObstacles([ [6, 1], [6, 2], [6, 3] ])
AddObstacles([ [8, 3], [8, 4], [8, 5] ])
# Find shortest path from [1,1] to [10,6]
ShortestPath([1, 1], [10, 6])
Show()
}illustration
The A* algorithm, used internally by ShortestPath() function generates an optimal path around obstacles:
1 2 3 4 5 6 7 8 9 0 ╭─v───────────────────╮ 1 > x . . . . ■ . . . . │ 2 │ ○ . ■ . . ■ . . . . │ 3 │ ○ . ■ . . ■ . ■ . . │ 4 │ ○ . ■ . . . . ■ . . │ 5 │ ○ . . . . . . ■ . . │ 6 │ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ │ ← Path shown as '○' for each traversed node ╰─────────────────────╯
Manhattan Path - Efficient Grid Movement
stzGrid enables automatic path definition using a Manhattan traversal strategy—either horizontal-first or vertical-first.
StzGridQ([10, 6]) {
# Horizontal-first Manhattan path
ManhattanPath([1, 1], [10, 6])
Show()
}illustration
This creates a path that moves horizontally first, then vertically:
1 2 3 4 5 6 7 8 9 0 ╭─v───────────────────╮ 1 > x ○ ○ ○ ○ ○ ○ ○ ○ ○ │ ← Move horizontally first 2 │ . . . . . . . . . ○ │ 3 │ . . . . . . . . . ○ │ ← Then move vertically 4 │ . . . . . . . . . ○ │ 5 │ . . . . . . . . . ○ │ 6 │ . . . . . . . . . ○ │ ╰─────────────────────╯
To let it move vertically-first, use ManhattanPathXT([1, 1], [10, 6], :Vertical) and you get:
1 2 3 4 5 6 7 8 9 0 ╭─v───────────────────╮ 1 > x . . . . . . . . . │ ← Move vertically first 2 │ ○ . . . . . . . . . │ 3 │ ○ . . . . . . . . . │ 4 │ ○ . . . . . . . . . │ 5 │ ○ . . . . . . . . . │ 6 │ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ │ ← Then move horizontally ╰─────────────────────╯
Note : The name "Manhattan distance" comes from the layout of Manhattan, New York City, which is famous for its grid-like street system. In this grid, streets typically run north-south and east-west, forming a rectangular pattern. When navigating such a city, a person or vehicle usually cannot move diagonally—only along the streets—so the shortest path between two points is the sum of the horizontal and vertical distances, not the straight-line (diagonal) distance.
Special Path Types: Spiral and ZigZag
Other advanced traversal strategies that may be useful in practice—such as Spiral and ZigZag—are also available.
StzGridQ([10, 6]) {
# Create spiral path with 3 rings
SpiralPath([5, 3], :Rings = 3)
Show()
}illustration
This creates a spiral pattern:
1 2 3 4 5 6 7 8 9 0 ╭─v───────────────────╮ 1 > x . ○ ○ ○ ○ ○ . . . │ 2 │ . . ○ ○ ○ ○ ○ . . . │ 3 │ . . ○ ○ ● ○ ○ . . . │ ← Spiral pattern from center 4 │ . . ○ ○ ○ ○ ○ . . . │ 5 │ . . ○ ○ ○ ○ ○ . . . │ 6 │ . . . . . . . . . . │ ╰─────────────────────╯
Region Analysis: Understanding Grid Connectivity
stzGrid provides powerful analysis of connected regions—defined as contiguous nodes enclosed by borders and/or closed obstacles.
o1 = new stzGrid([10, 6]) # Create barriers dividing the grid o1.AddObstacles([ [3, 1], [3, 2], [3, 3], [3, 4], [3, 6], [7, 1], [7, 2], [7, 3], [7, 4], [7, 5], [7, 6] ]) o1.Show()
illustration
This creates two separate regions:
1 2 3 4 5 6 7 8 9 0 ╭─v───────────────────╮ 1 > x . ■ . . . ■ . . . │ 2 │ . . ■ . . . ■ . . . │ 3 │ . . ■ . . . ■ . . . │ ← Two vertical barrier lines 4 │ . . ■ . . . ■ . . . │ 5 │ . . . . . . ■ . . . │ 6 │ . . ■ . . . ■ . . . │ ╰─────────────────────╯
Let's analyze the connectivity of various node pairs within such a grid structure:
# Test connectivity ? o1.AreConnected([1, 1], [2, 2]) #--> TRUE - same region ? o1.AreConnected([1, 1], [8, 6]) #--> FALSE - different regions # Find and paint all connected regions o1.ShowRegions()
illustration
This visually identifies the regions:
1 2 3 4 5 6 7 8 9 0 ╭─v───────────────────╮ 1 > 1 1 ■ 1 1 1 ■ 2 2 2 │ 2 │ 1 1 ■ 1 1 1 ■ 2 2 2 │ 3 │ 1 1 ■ 1 1 1 ■ 2 2 2 │ ← Regions automatically labeled '1' and '2' 4 │ 1 1 ■ 1 1 1 ■ 2 2 2 │ 5 │ 1 1 1 1 1 1 ■ 2 2 2 │ 6 │ 1 1 ■ 1 1 1 ■ 2 2 2 │ ╰─────────────────────╯
If you want, you can paint the regions with your own characters, using the eXTended form of the same function:
o1.ShowRegionsXT(["A", "B"])
#-->
1 2 3 4 5 6 7 8 9 0
╭─v───────────────────╮
1 > A A ■ A A A ■ B B B │
2 │ A A ■ A A A ■ B B B │
3 │ A A ■ A A A ■ B B B │ ← Regions labeled 'A' and 'B'
4 │ A A ■ A A A ■ B B B │
5 │ A A A A A A ■ B B B │
6 │ A A ■ A A A ■ B B B │
╰─────────────────────╯
Maze Generation: Creating Complex Navigation Challenges
stzGrid provides built-in gamified maze generation capabilities:
StzGridQ([15, 8]) {
# Generate a random maze with 30% obstacle density
RandomMaze(30)
Show()
}illustration
This might produce:
1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 ╭─v─────────────────────────────╮ 1 > x . . . . ■ . ■ . . ■ ■ ■ ■ . │ 2 │ . . . ■ . . . . . ■ ■ . . . . │ 3 │ . . ■ . ■ . . . . . ■ . . . ■ │ ← Random maze with obstacles 4 │ . . . ■ . . . . . . . . . . ■ │ 5 │ . . ■ ■ ■ ■ . . ■ . . . . ■ ■ │ 6 │ . ■ ■ . . . . . . . . . . ■ . │ 7 │ ■ ■ . . ■ . ■ . . ■ ■ . ■ . . │ 8 │ . ■ . . . . . . ■ . . . . ■ . │ ╰───────────────────────────────╯
Or create a maze with a guaranteed path from start to end:
StzGridQ([15, 8]) {
# Generate maze with guaranteed path from [1,1] to [15,8]
MazeWithPath([1, 1], [15, 8])
Show()
}illustration
This creates a maze with a valid solution that its guranteed to traverse the defined path:
1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 ╭─v─────────────────────────────╮ 1 > x . . ■ . . . . ■ ■ . ■ . . . │ 2 │ ○ . ■ . . . . . ■ . . . . . . │ 3 │ ○ . . . . . . . ■ . . . ■ . ■ │ 4 │ ○ ■ . . ■ . ■ ■ . . . . ■ . . │ 5 │ ○ . . . ■ ■ ■ . ■ . ■ . . . . │ 6 │ ○ . . ■ ■ . . . . . . . . . . │ 7 │ ○ . ■ . ■ . . . . ■ . . . . . │ 8 │ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ │ ← Path shown by '○' ╰───────────────────────────────╯
Path Complexity Analysis: Measuring Path Quality
Beyond basic path creation and finding, stzGrid offers sophisticated tools to analyze path characteristics, helping you evaluate and optimize your navigation solutions.
Understanding Path Complexity
The PathComplexity() method counts the number of direction changes or "turns" in a path, providing a quantitative measure of navigational complexity:
StzGridQ([8, 6]) {
# Create a simple path with several turns
AddPath([
[1, 1], [2, 1], [3, 1], # Move right
[3, 2], [3, 3], # Move down
[4, 3], [5, 3], [6, 3], # Move right again
[6, 4], [6, 5] # Move down again
])
# Check path complexity
? PathComplexity() #--> 3 (Three turns in the path)
Show()
}illustration
This creates a path with distinct turns:
1 2 3 4 5 6 7 8 ╭─v───────────────╮ 1 > ○ ○ ○ · · · · · │ 2 │ · · ○ · · · · · │ ← First turn (right to down) 3 │ · · ○ ○ ○ ○ · · │ ← Second turn (down to right) 4 │ · · · · · ○ · · │ ← Third turn (right to down) 5 │ · · · · · ○ · · │ 6 │ · · · · · · · · │ ╰─────────────────╯
When evaluating path complexity:
- 0-1 turns: Simple, direct paths suitable for straight-line navigation
- 2-4 turns: Moderately complex paths with reasonable navigation demands
- 5+ turns: Complex paths that may require more detailed instructions or waypoints
The complexity value lets you compare multiple paths objectively—a path with 2 turns is inherently simpler to navigate than one with 6 turns covering similar distance.
Measuring Path Efficiency
The PathEfficiency() method calculates how efficient a path is compared to the direct Manhattan distance, returning a percentage value:
StzGridQ([10, 6]) {
# First path - direct and efficient
AddPath([
[1, 1], [2, 1], [3, 1], [4, 1], [5, 1],
[5, 2], [5, 3]
])
? PathEfficiency() #--> 100.0 (Perfect efficiency)
Show()
}illustration
When visualized, the efficient path looks like this:
1 2 3 4 5 6 7 8 9 0 ╭─v───────────────────╮ 1 > ○ ○ ○ ○ ○ · · · · · │ 2 │ · · · · ○ · · · · · │ ← Direct path with no wasted steps 3 │ · · · · ○ · · · · · │ 4 │ · · · · · · · · · · │ 5 │ · · · · · · · · · · │ 6 │ · · · · · · · · · · │ ╰─────────────────────╯
Now let's examine a less efficient path:
StzGridQ([10, 6]) {
# Second path - winding and less efficient
AddPath([
[1, 1], [2, 1], [3, 1],
[3, 2], [2, 2], [2, 3],
[3, 3], [4, 3], [5, 3]
])
? PathEfficiency() #--> 75.0 (Less efficient)
Show()
}illustration
This creates a meandering path with lower efficiency:
1 2 3 4 5 6 7 8 9 0 ╭─v───────────────────╮ 1 > ○ ○ ○ · · · · · · · │ 2 │ · ○ ○ · · · · · · · │ ← Path loops back on itself 3 │ · ○ ○ ○ ○ · · · · · │ 4 │ · · · · · · · · · · │ 5 │ · · · · · · · · · · │ 6 │ · · · · · · · · · · │ ╰─────────────────────╯
Path efficiency provides a quantitative basis for comparing navigation solutions:
- 100%: Optimal path with no wasted movement
- 75-99%: Good efficiency, acceptable for most applications
- 50-74%: Moderate efficiency, may need optimization
- Below 50%: Poor efficiency, likely contains unnecessary detours
These analysis tools provide valuable metrics for evaluating and refining navigation strategies.
Practical Applications of stzGrid
As you could imagine, the stzGrid class offers solutions for a wide variety of practical problems:
Pathfinding and Navigation
- Robot Navigation: Plan efficient paths for robots in warehouses
- Traffic Simulation: Model vehicle movement through city grids
- Evacuation Planning: Design optimal exit routes for buildings
Path Analysis and Optimization
- Path Complexity Analysis: Quantify navigation complexity by measuring turns and direction changes
- Efficiency Benchmarking: Compare path solutions based on optimality metrics
- NPC Movement Enhancement: Design more natural character movement patterns in games
- Transportation Optimization: Design routes with fewer turns to reduce congestion
- UX Flow Improvement: Analyze and optimize user journeys by quantifying navigation steps
Game Development
- Board Games: Create chess, checkers, or Go boards with position tracking
- Roguelike Games: Generate procedural dungeons with guaranteed paths
- Strategy Games: Implement unit movement with pathfinding on terrain maps
- Puzzle Games: Create grid-based puzzles like Sokoban or sliding puzzles
Note: In this particular case, using
stzTile—the square-based counterpart ofstzGrid—would be more suitable, as it aligns better with the map metaphor commonly used in platform games. See examples in thestzTileTest.ringfile : here(opens in a new tab).
Simulations
- Cellular Automata: Implement Conway's Game of Life or other cellular automata
- Flow Simulations: Model fluid dynamics on a grid
- Population Models: Create agent-based simulations with movement rules
Image Processing
- Flood Fill: Implement paint bucket tools
- Edge Detection: Find boundaries between regions
- Region Labeling: Identify connected components in binary images
Educational Tools
- Maze Solving Visualizations: Demonstrate different pathfinding algorithms
- Graph Theory Concepts: Visualize connectivity and spanning trees
- Algorithm Animation: Show step-by-step execution of grid-based algorithms
The Softanza Advantage: Why stzGrid Stands Out
When compared to similar grid implementations in other languages and frameworks, stzGrid offers several distinct advantages:
| Feature | stzGrid (Softanza) | Python (NumPy/networkx) | JavaScript (Grid Libraries) | C++ (Grid Implementations) |
|---|---|---|---|---|
| API Intuitiveness | ✅ Very high - methods like MoveRight() are self-explanatory |
Moderate - requires understanding of array indexing | Varies by library | Typically low - requires understanding of implementation details |
| Visualization | ✅ Built-in ASCII visualization | Requires matplotlib or other visualization libraries | Requires DOM manipulation or canvas | Requires external visualization libraries |
| Pathfinding | ✅ Multiple built-in algorithms (A*, Manhattan, ZigZag, Spiral) | Requires networkx or custom implementation | Usually requires separate libraries | Requires custom implementation or libraries |
| Obstacle Handling | ✅ Native support with collision detection | Must be manually implemented | Limited in most libraries | Must be manually implemented |
| Region Analysis | ✅ Built-in flood fill and connectivity testing | Requires scipy or custom algorithms | Usually limited | Requires custom implementation |
| Movement Paradigm | ✅ Rich directional movement with boundary awareness | Basic array indexing | Basic grid movement | Basic coordinate management |
| Maze Generation | ✅ Built-in algorithms | Requires third-party libraries | Varies by library | Requires custom implementation |
| Learning Curve | ✅ Gentle - intuitive naming conventions | Steeper - requires understanding of array operations | Varies by library | Steep - requires deep programming knowledge |
| Code Readability | ✅ High - o1.MoveToNextPosition() is self-documenting |
Moderate - grid[x+1][y] requires context |
Varies by library | Often low due to implementation complexity |
Conclusion: The Power of Grid Navigation
The stzGrid class offers a practical and flexible solution for working with grids, making navigation and analysis both intuitive and efficient. Like anything else in Softanza library, Its API is designed to simplify complex operations, turning what’s usually verbose and error-prone into clean, readable code.
By abstracting the complexity of grid-based operations—such as movement, pathfinding, obstacle handling, and region analysis—stzGrid makes it easier to integrate into games, simulations, or spatial computing tasks without getting bogged down in low-level details.
Cite
Mansour Ayouni. "The Navigator's Grid: Mastering stzGrid in the Softanza Library". Softanza narrations, 2025. https://mayouni.github.io/stzsite/en/narrations/stzgrid-navigator-grid-mastering.html
Not run: its code touches files, the network, the clock or chance. The article is published as written; its blocks are illustrations. The code of the articles is Haro code. Where an article was written before the language took its present name, the name was changed and nothing else. Genre, area and series read from the file's name, the year from its first commit: derived, until the article carries its own header. The file on GitHub(opens in a new tab)

