search-library 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- search_library-0.1.0/.github/workflows/cd.yml +63 -0
- search_library-0.1.0/.github/workflows/ci.yml +68 -0
- search_library-0.1.0/.gitignore +42 -0
- search_library-0.1.0/.python-version +1 -0
- search_library-0.1.0/CHANGELOG.md +33 -0
- search_library-0.1.0/LICENSE +21 -0
- search_library-0.1.0/PKG-INFO +209 -0
- search_library-0.1.0/README.md +180 -0
- search_library-0.1.0/pyproject.toml +109 -0
- search_library-0.1.0/src/search_library/__init__.py +21 -0
- search_library-0.1.0/src/search_library/algorithms/__init__.py +5 -0
- search_library-0.1.0/src/search_library/algorithms/astar.py +126 -0
- search_library-0.1.0/src/search_library/core/__init__.py +13 -0
- search_library-0.1.0/src/search_library/core/nodes.py +57 -0
- search_library-0.1.0/src/search_library/core/problem.py +65 -0
- search_library-0.1.0/src/search_library/core/result.py +37 -0
- search_library-0.1.0/src/search_library/core/state.py +22 -0
- search_library-0.1.0/src/search_library/exceptions/__init__.py +17 -0
- search_library-0.1.0/src/search_library/exceptions/exceptions.py +63 -0
- search_library-0.1.0/src/search_library/graph/__init__.py +6 -0
- search_library-0.1.0/src/search_library/graph/edges.py +37 -0
- search_library-0.1.0/src/search_library/graph/graph.py +192 -0
- search_library-0.1.0/src/search_library/grid/__init__.py +6 -0
- search_library-0.1.0/src/search_library/grid/grid.py +228 -0
- search_library-0.1.0/src/search_library/grid/grid_search.py +79 -0
- search_library-0.1.0/src/search_library/heuristics/__init__.py +11 -0
- search_library-0.1.0/src/search_library/heuristics/base.py +43 -0
- search_library-0.1.0/src/search_library/heuristics/euclidean.py +34 -0
- search_library-0.1.0/src/search_library/heuristics/manhattan.py +29 -0
- search_library-0.1.0/src/search_library/py.typed +0 -0
- search_library-0.1.0/src/search_library/utils/__init__.py +1 -0
- search_library-0.1.0/src/search_library/utils/helpers.py +44 -0
- search_library-0.1.0/tests/__init__.py +1 -0
- search_library-0.1.0/tests/test_core.py +93 -0
- search_library-0.1.0/tests/test_exceptions.py +60 -0
- search_library-0.1.0/tests/test_graph.py +195 -0
- search_library-0.1.0/tests/test_grid.py +274 -0
- search_library-0.1.0/tests/test_heuristics.py +74 -0
- search_library-0.1.0/tests/test_utils.py +54 -0
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
name: CD
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
workflow_dispatch:
|
|
7
|
+
|
|
8
|
+
permissions:
|
|
9
|
+
contents: write
|
|
10
|
+
id-token: write
|
|
11
|
+
|
|
12
|
+
jobs:
|
|
13
|
+
ci:
|
|
14
|
+
uses: ./.github/workflows/ci.yml
|
|
15
|
+
permissions:
|
|
16
|
+
contents: read
|
|
17
|
+
|
|
18
|
+
release:
|
|
19
|
+
needs: ci
|
|
20
|
+
runs-on: ubuntu-latest
|
|
21
|
+
concurrency:
|
|
22
|
+
group: release
|
|
23
|
+
cancel-in-progress: false
|
|
24
|
+
|
|
25
|
+
permissions:
|
|
26
|
+
contents: write
|
|
27
|
+
id-token: write
|
|
28
|
+
|
|
29
|
+
steps:
|
|
30
|
+
- uses: actions/checkout@v4
|
|
31
|
+
with:
|
|
32
|
+
fetch-depth: 0
|
|
33
|
+
token: ${{ secrets.GITHUB_TOKEN }}
|
|
34
|
+
|
|
35
|
+
- name: Install uv
|
|
36
|
+
uses: astral-sh/setup-uv@v4
|
|
37
|
+
with:
|
|
38
|
+
version: "latest"
|
|
39
|
+
|
|
40
|
+
- name: Set up Python
|
|
41
|
+
run: uv python install 3.11
|
|
42
|
+
|
|
43
|
+
- name: Install dependencies
|
|
44
|
+
run: uv sync
|
|
45
|
+
|
|
46
|
+
- name: Python Semantic Release
|
|
47
|
+
id: semantic
|
|
48
|
+
uses: python-semantic-release/python-semantic-release@v9
|
|
49
|
+
with:
|
|
50
|
+
github_token: ${{ secrets.GITHUB_TOKEN }}
|
|
51
|
+
|
|
52
|
+
- name: Publish to PyPI
|
|
53
|
+
if: steps.semantic.outputs.released == 'true'
|
|
54
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
55
|
+
with:
|
|
56
|
+
attestations: true
|
|
57
|
+
|
|
58
|
+
- name: Publish GitHub Release
|
|
59
|
+
if: steps.semantic.outputs.released == 'true'
|
|
60
|
+
uses: python-semantic-release/publish-action@v9
|
|
61
|
+
with:
|
|
62
|
+
github_token: ${{ secrets.GITHUB_TOKEN }}
|
|
63
|
+
tag: ${{ steps.semantic.outputs.tag }}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main, develop]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [main]
|
|
8
|
+
workflow_call:
|
|
9
|
+
|
|
10
|
+
permissions:
|
|
11
|
+
contents: read
|
|
12
|
+
|
|
13
|
+
jobs:
|
|
14
|
+
lint-and-type-check:
|
|
15
|
+
runs-on: ubuntu-latest
|
|
16
|
+
steps:
|
|
17
|
+
- uses: actions/checkout@v4
|
|
18
|
+
|
|
19
|
+
- name: Install uv
|
|
20
|
+
uses: astral-sh/setup-uv@v4
|
|
21
|
+
with:
|
|
22
|
+
version: "latest"
|
|
23
|
+
|
|
24
|
+
- name: Set up Python 3.11
|
|
25
|
+
run: uv python install 3.11
|
|
26
|
+
|
|
27
|
+
- name: Install dependencies
|
|
28
|
+
run: uv sync
|
|
29
|
+
|
|
30
|
+
- name: Run Ruff linting
|
|
31
|
+
run: uv run ruff check src/ tests/
|
|
32
|
+
|
|
33
|
+
- name: Run Ruff formatting check
|
|
34
|
+
run: uv run ruff format --check src/ tests/
|
|
35
|
+
|
|
36
|
+
- name: Run MyPy type checking
|
|
37
|
+
run: uv run mypy src/
|
|
38
|
+
|
|
39
|
+
test:
|
|
40
|
+
runs-on: ubuntu-latest
|
|
41
|
+
strategy:
|
|
42
|
+
matrix:
|
|
43
|
+
python-version: ["3.11", "3.12", "3.13", "3.14"]
|
|
44
|
+
fail-fast: false
|
|
45
|
+
|
|
46
|
+
steps:
|
|
47
|
+
- uses: actions/checkout@v4
|
|
48
|
+
|
|
49
|
+
- name: Install uv
|
|
50
|
+
uses: astral-sh/setup-uv@v4
|
|
51
|
+
with:
|
|
52
|
+
version: "latest"
|
|
53
|
+
|
|
54
|
+
- name: Set up Python ${{ matrix.python-version }}
|
|
55
|
+
run: uv python install ${{ matrix.python-version }}
|
|
56
|
+
|
|
57
|
+
- name: Install dependencies
|
|
58
|
+
run: uv sync --python ${{ matrix.python-version }}
|
|
59
|
+
|
|
60
|
+
- name: Run tests with coverage
|
|
61
|
+
run: uv run pytest --cov=search_library --cov-report=xml --cov-report=term-missing --cov-fail-under=90
|
|
62
|
+
|
|
63
|
+
- name: Upload coverage report
|
|
64
|
+
if: matrix.python-version == '3.11'
|
|
65
|
+
uses: actions/upload-artifact@v4
|
|
66
|
+
with:
|
|
67
|
+
name: coverage-report
|
|
68
|
+
path: coverage.xml
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# Python-generated files
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[oc]
|
|
4
|
+
build/
|
|
5
|
+
dist/
|
|
6
|
+
wheels/
|
|
7
|
+
*.egg-info/
|
|
8
|
+
*.egg
|
|
9
|
+
|
|
10
|
+
# Virtual environments
|
|
11
|
+
.venv/
|
|
12
|
+
venv/
|
|
13
|
+
env/
|
|
14
|
+
|
|
15
|
+
# IDE
|
|
16
|
+
.idea/
|
|
17
|
+
.vscode/
|
|
18
|
+
*.swp
|
|
19
|
+
*.swo
|
|
20
|
+
|
|
21
|
+
# Testing & Coverage
|
|
22
|
+
htmlcov/
|
|
23
|
+
.coverage
|
|
24
|
+
.coverage.*
|
|
25
|
+
coverage.xml
|
|
26
|
+
*.cover
|
|
27
|
+
.pytest_cache/
|
|
28
|
+
.mypy_cache/
|
|
29
|
+
|
|
30
|
+
# Distribution / Packaging
|
|
31
|
+
*.whl
|
|
32
|
+
*.tar.gz
|
|
33
|
+
|
|
34
|
+
# UV
|
|
35
|
+
uv.lock
|
|
36
|
+
|
|
37
|
+
# OS
|
|
38
|
+
.DS_Store
|
|
39
|
+
Thumbs.db
|
|
40
|
+
|
|
41
|
+
# Ruff
|
|
42
|
+
.ruff_cache/
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
3.11
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# CHANGELOG
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
## v0.1.0 (2026-06-20)
|
|
5
|
+
|
|
6
|
+
### Bug Fixes
|
|
7
|
+
|
|
8
|
+
- Formatting for grid.py and add workflow_call trigger to CI
|
|
9
|
+
([`8d75c31`](https://github.com/ahincho/search-library/commit/8d75c314464b2b2090786a66ae4c3785c839d0d1))
|
|
10
|
+
|
|
11
|
+
- Use pip+build instead of uv in semantic-release container
|
|
12
|
+
([`a740379`](https://github.com/ahincho/search-library/commit/a74037961a15263a40bc9b86740afb2f25a9d6a7))
|
|
13
|
+
|
|
14
|
+
### Chores
|
|
15
|
+
|
|
16
|
+
- Add workflow_dispatch trigger to CD for manual releases
|
|
17
|
+
([`15c5bb0`](https://github.com/ahincho/search-library/commit/15c5bb020e562d4ea9906c190cffacde2a321349))
|
|
18
|
+
|
|
19
|
+
### Documentation
|
|
20
|
+
|
|
21
|
+
- Improve README with badges, examples, architecture and roadmap
|
|
22
|
+
([`e6e21ab`](https://github.com/ahincho/search-library/commit/e6e21abd928097180bd5ad6b1f81c2820a7f9fb6))
|
|
23
|
+
|
|
24
|
+
### Features
|
|
25
|
+
|
|
26
|
+
- Initial implementation of search-library with A* algorithm - Core abstractions: Node, State,
|
|
27
|
+
SearchProblem, SearchResult - A* search algorithm with heapq priority queue (f=g+h) - Heuristics:
|
|
28
|
+
Manhattan, Euclidean, extensible base ABC - Graph support: weighted directed/undirected graphs -
|
|
29
|
+
Grid support: 2D pathfinding with obstacles, 4/8 directions - Custom exceptions and utility
|
|
30
|
+
helpers - Full test suite (92 tests, 98.87% coverage) - Ruff + MyPy strict configuration - CI/CD:
|
|
31
|
+
GitHub Actions (matrix 3.11-3.14) + semantic-release + PyPI - pyproject.toml with complete PyPI
|
|
32
|
+
metadata (MIT license)
|
|
33
|
+
([`ae2c4e7`](https://github.com/ahincho/search-library/commit/ae2c4e794f1312e2297237ce0ebd3093908b8fc4))
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 ahincho
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: search-library
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A professional, extensible search algorithm framework for discrete spaces (graphs and grids). Implements A* with pluggable heuristics.
|
|
5
|
+
Project-URL: Homepage, https://github.com/ahincho/search-library
|
|
6
|
+
Project-URL: Repository, https://github.com/ahincho/search-library
|
|
7
|
+
Project-URL: Issues, https://github.com/ahincho/search-library/issues
|
|
8
|
+
Project-URL: Documentation, https://github.com/ahincho/search-library#readme
|
|
9
|
+
Author-email: ahincho <ahincho@unsa.edu.pe>
|
|
10
|
+
License-Expression: MIT
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
Keywords: a-star,algorithm,astar,graph,grid,heuristic,pathfinding,search
|
|
13
|
+
Classifier: Development Status :: 3 - Alpha
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: Intended Audience :: Education
|
|
16
|
+
Classifier: Intended Audience :: Science/Research
|
|
17
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
18
|
+
Classifier: Operating System :: OS Independent
|
|
19
|
+
Classifier: Programming Language :: Python :: 3
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
23
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
24
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
25
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
26
|
+
Classifier: Typing :: Typed
|
|
27
|
+
Requires-Python: >=3.11
|
|
28
|
+
Description-Content-Type: text/markdown
|
|
29
|
+
|
|
30
|
+
# search-library
|
|
31
|
+
|
|
32
|
+
[](https://github.com/ahincho/search-library/actions/workflows/ci.yml)
|
|
33
|
+
[](https://www.python.org/downloads/)
|
|
34
|
+
[](https://opensource.org/licenses/MIT)
|
|
35
|
+
[](https://github.com/astral-sh/ruff)
|
|
36
|
+
[](http://mypy-lang.org/)
|
|
37
|
+
[]()
|
|
38
|
+
|
|
39
|
+
A professional, extensible search algorithm framework for discrete spaces (graphs and 2D grids).
|
|
40
|
+
|
|
41
|
+
## Features
|
|
42
|
+
|
|
43
|
+
- **A\* Search Algorithm** — optimal pathfinding with `f(n) = g(n) + h(n)`
|
|
44
|
+
- **Graph support** — weighted directed/undirected graphs with adjacency lists
|
|
45
|
+
- **Grid support** — 2D pathfinding with obstacles, variable costs, 4/8-directional movement
|
|
46
|
+
- **Pluggable heuristics** — Manhattan, Euclidean, or bring your own
|
|
47
|
+
- **Extensible architecture** — designed for adding BFS, DFS, Dijkstra without modifying base code
|
|
48
|
+
- **Strict typing** — full `mypy --strict` compliance with Generics and Protocols
|
|
49
|
+
- **Zero runtime dependencies** — pure Python standard library only
|
|
50
|
+
|
|
51
|
+
## Installation
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
pip install search-library
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Or with uv:
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
uv add search-library
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## Quick Start
|
|
64
|
+
|
|
65
|
+
### Graph Search
|
|
66
|
+
|
|
67
|
+
```python
|
|
68
|
+
from search_library import Graph, AStarSearch
|
|
69
|
+
|
|
70
|
+
# Create an undirected weighted graph
|
|
71
|
+
graph = Graph[str](directed=False)
|
|
72
|
+
graph.add_edge("A", "B", 1.0)
|
|
73
|
+
graph.add_edge("B", "C", 2.0)
|
|
74
|
+
graph.add_edge("A", "C", 5.0)
|
|
75
|
+
|
|
76
|
+
# Solve with A*
|
|
77
|
+
problem = graph.to_search_problem("A", "C")
|
|
78
|
+
solver = AStarSearch(problem)
|
|
79
|
+
result = solver.search()
|
|
80
|
+
|
|
81
|
+
print(result.path) # ['A', 'B', 'C']
|
|
82
|
+
print(result.total_cost) # 3.0
|
|
83
|
+
print(result.nodes_explored) # 3
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
### Grid Pathfinding (Maze)
|
|
87
|
+
|
|
88
|
+
```python
|
|
89
|
+
from search_library import Grid, AStarSearch
|
|
90
|
+
from search_library.grid import GridSearchProblem
|
|
91
|
+
|
|
92
|
+
# 0 = walkable, 1 = obstacle
|
|
93
|
+
matrix = [
|
|
94
|
+
[0, 0, 0, 0, 0],
|
|
95
|
+
[0, 1, 1, 1, 0],
|
|
96
|
+
[0, 0, 0, 1, 0],
|
|
97
|
+
[0, 1, 0, 0, 0],
|
|
98
|
+
[0, 0, 0, 0, 0],
|
|
99
|
+
]
|
|
100
|
+
|
|
101
|
+
grid = Grid.from_matrix(matrix)
|
|
102
|
+
problem = GridSearchProblem(grid, start=(0, 0), goal=(4, 4))
|
|
103
|
+
solver = AStarSearch(problem)
|
|
104
|
+
result = solver.search()
|
|
105
|
+
|
|
106
|
+
print(result.success) # True
|
|
107
|
+
print(result.total_cost) # 8.0
|
|
108
|
+
print(result.nodes_explored) # Number of states explored
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
### Custom Heuristic
|
|
112
|
+
|
|
113
|
+
```python
|
|
114
|
+
from search_library.heuristics.base import Heuristic
|
|
115
|
+
|
|
116
|
+
class ChebyshevHeuristic(Heuristic[tuple[int, int]]):
|
|
117
|
+
"""Chebyshev distance — useful for 8-directional grids."""
|
|
118
|
+
def estimate(self, state: tuple[int, int], goal: tuple[int, int]) -> float:
|
|
119
|
+
return float(max(abs(state[0] - goal[0]), abs(state[1] - goal[1])))
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
### Custom Search Problem
|
|
123
|
+
|
|
124
|
+
```python
|
|
125
|
+
from search_library.core.problem import SearchProblem
|
|
126
|
+
from search_library.algorithms.astar import AStarSearch
|
|
127
|
+
|
|
128
|
+
class EightPuzzle(SearchProblem[tuple[int, ...]]):
|
|
129
|
+
"""Example: define any discrete search problem."""
|
|
130
|
+
def initial_state(self) -> tuple[int, ...]:
|
|
131
|
+
return (1, 2, 3, 4, 0, 5, 6, 7, 8)
|
|
132
|
+
|
|
133
|
+
def is_goal(self, state: tuple[int, ...]) -> bool:
|
|
134
|
+
return state == (1, 2, 3, 4, 5, 6, 7, 8, 0)
|
|
135
|
+
|
|
136
|
+
def successors(self, state: tuple[int, ...]) -> list[tuple[tuple[int, ...], float]]:
|
|
137
|
+
# Return list of (next_state, cost) tuples
|
|
138
|
+
...
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
## Architecture
|
|
142
|
+
|
|
143
|
+
```
|
|
144
|
+
src/search_library/
|
|
145
|
+
├── core/ # Node, State (Protocol), SearchProblem (ABC), SearchResult
|
|
146
|
+
├── algorithms/ # A* implementation (extensible for BFS, DFS, Dijkstra)
|
|
147
|
+
├── heuristics/ # Heuristic ABC + Manhattan + Euclidean
|
|
148
|
+
├── graph/ # Graph + Edge + GraphSearchProblem adapter
|
|
149
|
+
├── grid/ # Grid + GridSearchProblem adapter (4/8 directions)
|
|
150
|
+
├── utils/ # Formatting helpers
|
|
151
|
+
└── exceptions/ # SearchError hierarchy
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
### Design Principles
|
|
155
|
+
|
|
156
|
+
- **SOLID** — each class has a single responsibility; open for extension, closed for modification
|
|
157
|
+
- **Strategy Pattern** — heuristics are interchangeable at runtime
|
|
158
|
+
- **Adapter Pattern** — Graph and Grid adapt to the unified SearchProblem interface
|
|
159
|
+
- **Generic Types** — algorithms work with any hashable state type
|
|
160
|
+
|
|
161
|
+
## Development
|
|
162
|
+
|
|
163
|
+
```bash
|
|
164
|
+
# Install all dependencies (including dev tools)
|
|
165
|
+
uv sync
|
|
166
|
+
|
|
167
|
+
# Run tests with coverage
|
|
168
|
+
uv run pytest
|
|
169
|
+
|
|
170
|
+
# Lint
|
|
171
|
+
uv run ruff check src/ tests/
|
|
172
|
+
|
|
173
|
+
# Format
|
|
174
|
+
uv run ruff format src/ tests/
|
|
175
|
+
|
|
176
|
+
# Type check (strict mode)
|
|
177
|
+
uv run mypy src/
|
|
178
|
+
|
|
179
|
+
# Build wheel + sdist
|
|
180
|
+
uv build
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
## CI/CD
|
|
184
|
+
|
|
185
|
+
| Pipeline | Trigger | What it does |
|
|
186
|
+
|----------|---------|--------------|
|
|
187
|
+
| **CI** | push, PR | Ruff + MyPy + Pytest (matrix: 3.11, 3.12, 3.13, 3.14) |
|
|
188
|
+
| **CD** | push to main | CI + Semantic Release + PyPI publish |
|
|
189
|
+
|
|
190
|
+
Versioning follows [Conventional Commits](https://www.conventionalcommits.org/):
|
|
191
|
+
- `feat:` → minor version bump
|
|
192
|
+
- `fix:` → patch version bump
|
|
193
|
+
- `feat!:` / `BREAKING CHANGE:` → major version bump
|
|
194
|
+
|
|
195
|
+
## Roadmap
|
|
196
|
+
|
|
197
|
+
- [x] A* Search Algorithm
|
|
198
|
+
- [x] Manhattan & Euclidean heuristics
|
|
199
|
+
- [x] Graph support (directed/undirected, weighted)
|
|
200
|
+
- [x] Grid support (4/8 directions, obstacles)
|
|
201
|
+
- [ ] BFS (Breadth-First Search)
|
|
202
|
+
- [ ] DFS (Depth-First Search)
|
|
203
|
+
- [ ] Dijkstra's Algorithm
|
|
204
|
+
- [ ] Bidirectional Search
|
|
205
|
+
- [ ] Visualization utilities
|
|
206
|
+
|
|
207
|
+
## License
|
|
208
|
+
|
|
209
|
+
[MIT](LICENSE) — free for academic and commercial use.
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
# search-library
|
|
2
|
+
|
|
3
|
+
[](https://github.com/ahincho/search-library/actions/workflows/ci.yml)
|
|
4
|
+
[](https://www.python.org/downloads/)
|
|
5
|
+
[](https://opensource.org/licenses/MIT)
|
|
6
|
+
[](https://github.com/astral-sh/ruff)
|
|
7
|
+
[](http://mypy-lang.org/)
|
|
8
|
+
[]()
|
|
9
|
+
|
|
10
|
+
A professional, extensible search algorithm framework for discrete spaces (graphs and 2D grids).
|
|
11
|
+
|
|
12
|
+
## Features
|
|
13
|
+
|
|
14
|
+
- **A\* Search Algorithm** — optimal pathfinding with `f(n) = g(n) + h(n)`
|
|
15
|
+
- **Graph support** — weighted directed/undirected graphs with adjacency lists
|
|
16
|
+
- **Grid support** — 2D pathfinding with obstacles, variable costs, 4/8-directional movement
|
|
17
|
+
- **Pluggable heuristics** — Manhattan, Euclidean, or bring your own
|
|
18
|
+
- **Extensible architecture** — designed for adding BFS, DFS, Dijkstra without modifying base code
|
|
19
|
+
- **Strict typing** — full `mypy --strict` compliance with Generics and Protocols
|
|
20
|
+
- **Zero runtime dependencies** — pure Python standard library only
|
|
21
|
+
|
|
22
|
+
## Installation
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
pip install search-library
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Or with uv:
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
uv add search-library
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Quick Start
|
|
35
|
+
|
|
36
|
+
### Graph Search
|
|
37
|
+
|
|
38
|
+
```python
|
|
39
|
+
from search_library import Graph, AStarSearch
|
|
40
|
+
|
|
41
|
+
# Create an undirected weighted graph
|
|
42
|
+
graph = Graph[str](directed=False)
|
|
43
|
+
graph.add_edge("A", "B", 1.0)
|
|
44
|
+
graph.add_edge("B", "C", 2.0)
|
|
45
|
+
graph.add_edge("A", "C", 5.0)
|
|
46
|
+
|
|
47
|
+
# Solve with A*
|
|
48
|
+
problem = graph.to_search_problem("A", "C")
|
|
49
|
+
solver = AStarSearch(problem)
|
|
50
|
+
result = solver.search()
|
|
51
|
+
|
|
52
|
+
print(result.path) # ['A', 'B', 'C']
|
|
53
|
+
print(result.total_cost) # 3.0
|
|
54
|
+
print(result.nodes_explored) # 3
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### Grid Pathfinding (Maze)
|
|
58
|
+
|
|
59
|
+
```python
|
|
60
|
+
from search_library import Grid, AStarSearch
|
|
61
|
+
from search_library.grid import GridSearchProblem
|
|
62
|
+
|
|
63
|
+
# 0 = walkable, 1 = obstacle
|
|
64
|
+
matrix = [
|
|
65
|
+
[0, 0, 0, 0, 0],
|
|
66
|
+
[0, 1, 1, 1, 0],
|
|
67
|
+
[0, 0, 0, 1, 0],
|
|
68
|
+
[0, 1, 0, 0, 0],
|
|
69
|
+
[0, 0, 0, 0, 0],
|
|
70
|
+
]
|
|
71
|
+
|
|
72
|
+
grid = Grid.from_matrix(matrix)
|
|
73
|
+
problem = GridSearchProblem(grid, start=(0, 0), goal=(4, 4))
|
|
74
|
+
solver = AStarSearch(problem)
|
|
75
|
+
result = solver.search()
|
|
76
|
+
|
|
77
|
+
print(result.success) # True
|
|
78
|
+
print(result.total_cost) # 8.0
|
|
79
|
+
print(result.nodes_explored) # Number of states explored
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### Custom Heuristic
|
|
83
|
+
|
|
84
|
+
```python
|
|
85
|
+
from search_library.heuristics.base import Heuristic
|
|
86
|
+
|
|
87
|
+
class ChebyshevHeuristic(Heuristic[tuple[int, int]]):
|
|
88
|
+
"""Chebyshev distance — useful for 8-directional grids."""
|
|
89
|
+
def estimate(self, state: tuple[int, int], goal: tuple[int, int]) -> float:
|
|
90
|
+
return float(max(abs(state[0] - goal[0]), abs(state[1] - goal[1])))
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
### Custom Search Problem
|
|
94
|
+
|
|
95
|
+
```python
|
|
96
|
+
from search_library.core.problem import SearchProblem
|
|
97
|
+
from search_library.algorithms.astar import AStarSearch
|
|
98
|
+
|
|
99
|
+
class EightPuzzle(SearchProblem[tuple[int, ...]]):
|
|
100
|
+
"""Example: define any discrete search problem."""
|
|
101
|
+
def initial_state(self) -> tuple[int, ...]:
|
|
102
|
+
return (1, 2, 3, 4, 0, 5, 6, 7, 8)
|
|
103
|
+
|
|
104
|
+
def is_goal(self, state: tuple[int, ...]) -> bool:
|
|
105
|
+
return state == (1, 2, 3, 4, 5, 6, 7, 8, 0)
|
|
106
|
+
|
|
107
|
+
def successors(self, state: tuple[int, ...]) -> list[tuple[tuple[int, ...], float]]:
|
|
108
|
+
# Return list of (next_state, cost) tuples
|
|
109
|
+
...
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
## Architecture
|
|
113
|
+
|
|
114
|
+
```
|
|
115
|
+
src/search_library/
|
|
116
|
+
├── core/ # Node, State (Protocol), SearchProblem (ABC), SearchResult
|
|
117
|
+
├── algorithms/ # A* implementation (extensible for BFS, DFS, Dijkstra)
|
|
118
|
+
├── heuristics/ # Heuristic ABC + Manhattan + Euclidean
|
|
119
|
+
├── graph/ # Graph + Edge + GraphSearchProblem adapter
|
|
120
|
+
├── grid/ # Grid + GridSearchProblem adapter (4/8 directions)
|
|
121
|
+
├── utils/ # Formatting helpers
|
|
122
|
+
└── exceptions/ # SearchError hierarchy
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
### Design Principles
|
|
126
|
+
|
|
127
|
+
- **SOLID** — each class has a single responsibility; open for extension, closed for modification
|
|
128
|
+
- **Strategy Pattern** — heuristics are interchangeable at runtime
|
|
129
|
+
- **Adapter Pattern** — Graph and Grid adapt to the unified SearchProblem interface
|
|
130
|
+
- **Generic Types** — algorithms work with any hashable state type
|
|
131
|
+
|
|
132
|
+
## Development
|
|
133
|
+
|
|
134
|
+
```bash
|
|
135
|
+
# Install all dependencies (including dev tools)
|
|
136
|
+
uv sync
|
|
137
|
+
|
|
138
|
+
# Run tests with coverage
|
|
139
|
+
uv run pytest
|
|
140
|
+
|
|
141
|
+
# Lint
|
|
142
|
+
uv run ruff check src/ tests/
|
|
143
|
+
|
|
144
|
+
# Format
|
|
145
|
+
uv run ruff format src/ tests/
|
|
146
|
+
|
|
147
|
+
# Type check (strict mode)
|
|
148
|
+
uv run mypy src/
|
|
149
|
+
|
|
150
|
+
# Build wheel + sdist
|
|
151
|
+
uv build
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
## CI/CD
|
|
155
|
+
|
|
156
|
+
| Pipeline | Trigger | What it does |
|
|
157
|
+
|----------|---------|--------------|
|
|
158
|
+
| **CI** | push, PR | Ruff + MyPy + Pytest (matrix: 3.11, 3.12, 3.13, 3.14) |
|
|
159
|
+
| **CD** | push to main | CI + Semantic Release + PyPI publish |
|
|
160
|
+
|
|
161
|
+
Versioning follows [Conventional Commits](https://www.conventionalcommits.org/):
|
|
162
|
+
- `feat:` → minor version bump
|
|
163
|
+
- `fix:` → patch version bump
|
|
164
|
+
- `feat!:` / `BREAKING CHANGE:` → major version bump
|
|
165
|
+
|
|
166
|
+
## Roadmap
|
|
167
|
+
|
|
168
|
+
- [x] A* Search Algorithm
|
|
169
|
+
- [x] Manhattan & Euclidean heuristics
|
|
170
|
+
- [x] Graph support (directed/undirected, weighted)
|
|
171
|
+
- [x] Grid support (4/8 directions, obstacles)
|
|
172
|
+
- [ ] BFS (Breadth-First Search)
|
|
173
|
+
- [ ] DFS (Depth-First Search)
|
|
174
|
+
- [ ] Dijkstra's Algorithm
|
|
175
|
+
- [ ] Bidirectional Search
|
|
176
|
+
- [ ] Visualization utilities
|
|
177
|
+
|
|
178
|
+
## License
|
|
179
|
+
|
|
180
|
+
[MIT](LICENSE) — free for academic and commercial use.
|