pio-robo 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.
Files changed (73) hide show
  1. pio_robo-0.1.0/LICENSE +21 -0
  2. pio_robo-0.1.0/PKG-INFO +84 -0
  3. pio_robo-0.1.0/README.md +63 -0
  4. pio_robo-0.1.0/pyproject.toml +41 -0
  5. pio_robo-0.1.0/setup.cfg +4 -0
  6. pio_robo-0.1.0/src/pio_robo.egg-info/PKG-INFO +84 -0
  7. pio_robo-0.1.0/src/pio_robo.egg-info/SOURCES.txt +71 -0
  8. pio_robo-0.1.0/src/pio_robo.egg-info/dependency_links.txt +1 -0
  9. pio_robo-0.1.0/src/pio_robo.egg-info/entry_points.txt +2 -0
  10. pio_robo-0.1.0/src/pio_robo.egg-info/requires.txt +8 -0
  11. pio_robo-0.1.0/src/pio_robo.egg-info/top_level.txt +1 -0
  12. pio_robo-0.1.0/src/robo/__init__.py +1 -0
  13. pio_robo-0.1.0/src/robo/__main__.py +4 -0
  14. pio_robo-0.1.0/src/robo/cli.py +115 -0
  15. pio_robo-0.1.0/src/robo/commands.py +178 -0
  16. pio_robo-0.1.0/src/robo/project.py +48 -0
  17. pio_robo-0.1.0/src/robo/run.py +407 -0
  18. pio_robo-0.1.0/src/robo/solutions/__init__.py +1 -0
  19. pio_robo-0.1.0/src/robo/solutions/world1_0.py +13 -0
  20. pio_robo-0.1.0/src/robo/solutions/world1_1.py +13 -0
  21. pio_robo-0.1.0/src/robo/solutions/world1_2.py +14 -0
  22. pio_robo-0.1.0/src/robo/solutions/world1_3.py +14 -0
  23. pio_robo-0.1.0/src/robo/solutions/world1_4.py +14 -0
  24. pio_robo-0.1.0/src/robo/solutions/world1_5.py +14 -0
  25. pio_robo-0.1.0/src/robo/solutions/world1_6.py +13 -0
  26. pio_robo-0.1.0/src/robo/solutions/world1_7.py +7 -0
  27. pio_robo-0.1.0/src/robo/solutions/world1_8.py +7 -0
  28. pio_robo-0.1.0/src/robo/solutions/world1_9.py +7 -0
  29. pio_robo-0.1.0/src/robo/solutions/world2_0.py +7 -0
  30. pio_robo-0.1.0/src/robo/solutions/world2_1.py +7 -0
  31. pio_robo-0.1.0/src/robo/teacher_solutions/__init__.py +1 -0
  32. pio_robo-0.1.0/src/robo/teacher_solutions/world1_0.py +0 -0
  33. pio_robo-0.1.0/src/robo/teacher_solutions/world1_1.py +0 -0
  34. pio_robo-0.1.0/src/robo/teacher_solutions/world1_2.py +0 -0
  35. pio_robo-0.1.0/src/robo/teacher_solutions/world1_3.py +0 -0
  36. pio_robo-0.1.0/src/robo/teacher_solutions/world1_4.py +0 -0
  37. pio_robo-0.1.0/src/robo/teacher_solutions/world1_5.py +0 -0
  38. pio_robo-0.1.0/src/robo/teacher_solutions/world1_6.py +0 -0
  39. pio_robo-0.1.0/src/robo/teacher_solutions/world1_7.py +27 -0
  40. pio_robo-0.1.0/src/robo/teacher_solutions/world1_8.py +21 -0
  41. pio_robo-0.1.0/src/robo/teacher_solutions/world1_9.py +70 -0
  42. pio_robo-0.1.0/src/robo/teacher_solutions/world2_0.py +26 -0
  43. pio_robo-0.1.0/src/robo/teacher_solutions/world2_1.py +93 -0
  44. pio_robo-0.1.0/src/robo/templates/__init__.py +1 -0
  45. pio_robo-0.1.0/src/robo/templates/exercises/world1_0.py +1 -0
  46. pio_robo-0.1.0/src/robo/templates/exercises/world1_1.py +0 -0
  47. pio_robo-0.1.0/src/robo/templates/exercises/world1_2.py +1 -0
  48. pio_robo-0.1.0/src/robo/templates/exercises/world1_3.py +1 -0
  49. pio_robo-0.1.0/src/robo/templates/exercises/world1_4.py +1 -0
  50. pio_robo-0.1.0/src/robo/templates/exercises/world1_5.py +1 -0
  51. pio_robo-0.1.0/src/robo/templates/exercises/world1_6.py +1 -0
  52. pio_robo-0.1.0/src/robo/templates/exercises/world1_7.py +6 -0
  53. pio_robo-0.1.0/src/robo/templates/exercises/world1_8.py +4 -0
  54. pio_robo-0.1.0/src/robo/templates/exercises/world1_9.py +14 -0
  55. pio_robo-0.1.0/src/robo/templates/exercises/world2_0.py +3 -0
  56. pio_robo-0.1.0/src/robo/templates/exercises/world2_1.py +12 -0
  57. pio_robo-0.1.0/src/robo/world_tools.py +666 -0
  58. pio_robo-0.1.0/src/robo/worlds/__init__.py +1 -0
  59. pio_robo-0.1.0/src/robo/worlds/world1_0.py +13 -0
  60. pio_robo-0.1.0/src/robo/worlds/world1_1.py +13 -0
  61. pio_robo-0.1.0/src/robo/worlds/world1_2.py +14 -0
  62. pio_robo-0.1.0/src/robo/worlds/world1_3.py +14 -0
  63. pio_robo-0.1.0/src/robo/worlds/world1_4.py +14 -0
  64. pio_robo-0.1.0/src/robo/worlds/world1_5.py +0 -0
  65. pio_robo-0.1.0/src/robo/worlds/world1_6.py +13 -0
  66. pio_robo-0.1.0/src/robo/worlds/world1_7.py +7 -0
  67. pio_robo-0.1.0/src/robo/worlds/world1_8.py +7 -0
  68. pio_robo-0.1.0/src/robo/worlds/world1_9.py +7 -0
  69. pio_robo-0.1.0/src/robo/worlds/world2_0.py +7 -0
  70. pio_robo-0.1.0/src/robo/worlds/world2_1.py +7 -0
  71. pio_robo-0.1.0/tests/test_commands.py +427 -0
  72. pio_robo-0.1.0/tests/test_display.py +200 -0
  73. pio_robo-0.1.0/tests/test_runner.py +75 -0
pio_robo-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) [2026] [Edward Withers]
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,84 @@
1
+ Metadata-Version: 2.4
2
+ Name: pio-robo
3
+ Version: 0.1.0
4
+ Summary: A simple grid-based robot programming environment for teaching Python functions-first.
5
+ Author: Edward Withers
6
+ License: MIT
7
+ Classifier: Programming Language :: Python :: 3
8
+ Classifier: Intended Audience :: Education
9
+ Classifier: Topic :: Education
10
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
11
+ Requires-Python: >=3.9
12
+ Description-Content-Type: text/markdown
13
+ License-File: LICENSE
14
+ Provides-Extra: student
15
+ Requires-Dist: pytest<10,>=8; extra == "student"
16
+ Provides-Extra: dev
17
+ Requires-Dist: pytest<10,>=8; extra == "dev"
18
+ Requires-Dist: build; extra == "dev"
19
+ Requires-Dist: twine; extra == "dev"
20
+ Dynamic: license-file
21
+
22
+ # šŸ¤– Robo – Classroom Programming Environment
23
+
24
+ A simple Python-based learning environment for teaching programming functions-first through grid-based robot challenges.
25
+
26
+ ## Setup
27
+
28
+ ### Install from PyPI
29
+
30
+ ```bash
31
+ pip install robo
32
+ ```
33
+
34
+ ### Development install
35
+
36
+ ```bash
37
+ git clone <repo>
38
+ cd robo
39
+ pip install -e .
40
+ ```
41
+
42
+ ## Run a world
43
+
44
+ From a student project folder:
45
+
46
+ ```bash
47
+ python -m robo world1.0
48
+ ```
49
+
50
+ Student solution files live in:
51
+
52
+ ```text
53
+ exercises/
54
+ world1_0.py
55
+ ```
56
+
57
+ ## Check progress
58
+
59
+ ```bash
60
+ python -m robo summary
61
+ ```
62
+
63
+ ## Run tests
64
+
65
+ From the project root:
66
+
67
+ ```bash
68
+ python -m pytest
69
+ ```
70
+
71
+ Run tests when you:
72
+
73
+ - add new worlds
74
+ - change commands
75
+ - change runner/display behaviour
76
+
77
+ ## Contribute
78
+
79
+ 1. Create or edit code.
80
+ 2. Run the test suite.
81
+ 3. Check that student worlds still run.
82
+ 4. Open a pull request.
83
+
84
+ See the `docs/` folder for pupil-facing command guides and teacher/developer notes.
@@ -0,0 +1,63 @@
1
+ # šŸ¤– Robo – Classroom Programming Environment
2
+
3
+ A simple Python-based learning environment for teaching programming functions-first through grid-based robot challenges.
4
+
5
+ ## Setup
6
+
7
+ ### Install from PyPI
8
+
9
+ ```bash
10
+ pip install robo
11
+ ```
12
+
13
+ ### Development install
14
+
15
+ ```bash
16
+ git clone <repo>
17
+ cd robo
18
+ pip install -e .
19
+ ```
20
+
21
+ ## Run a world
22
+
23
+ From a student project folder:
24
+
25
+ ```bash
26
+ python -m robo world1.0
27
+ ```
28
+
29
+ Student solution files live in:
30
+
31
+ ```text
32
+ exercises/
33
+ world1_0.py
34
+ ```
35
+
36
+ ## Check progress
37
+
38
+ ```bash
39
+ python -m robo summary
40
+ ```
41
+
42
+ ## Run tests
43
+
44
+ From the project root:
45
+
46
+ ```bash
47
+ python -m pytest
48
+ ```
49
+
50
+ Run tests when you:
51
+
52
+ - add new worlds
53
+ - change commands
54
+ - change runner/display behaviour
55
+
56
+ ## Contribute
57
+
58
+ 1. Create or edit code.
59
+ 2. Run the test suite.
60
+ 3. Check that student worlds still run.
61
+ 4. Open a pull request.
62
+
63
+ See the `docs/` folder for pupil-facing command guides and teacher/developer notes.
@@ -0,0 +1,41 @@
1
+ [build-system]
2
+ requires = ["setuptools>=61.0"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "pio-robo"
7
+ version = "0.1.0"
8
+ description = "A simple grid-based robot programming environment for teaching Python functions-first."
9
+ readme = "README.md"
10
+ requires-python = ">=3.9"
11
+ authors = [
12
+ { name = "Edward Withers" }
13
+ ]
14
+ license = { text = "MIT" }
15
+ classifiers = [
16
+ "Programming Language :: Python :: 3",
17
+ "Intended Audience :: Education",
18
+ "Topic :: Education",
19
+ "Topic :: Software Development :: Libraries :: Python Modules"
20
+ ]
21
+
22
+
23
+ [project.optional-dependencies]
24
+ student = [
25
+ "pytest>=8,<10",
26
+ ]
27
+
28
+ dev = [
29
+ "pytest>=8,<10",
30
+ "build",
31
+ "twine",
32
+ ]
33
+
34
+ [project.scripts]
35
+ robo = "robo.cli:main"
36
+
37
+ [tool.setuptools.packages.find]
38
+ where = ["src"]
39
+
40
+ [tool.setuptools.package-data]
41
+ "robo.templates" = ["README.md", "exercises/*.py"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,84 @@
1
+ Metadata-Version: 2.4
2
+ Name: pio-robo
3
+ Version: 0.1.0
4
+ Summary: A simple grid-based robot programming environment for teaching Python functions-first.
5
+ Author: Edward Withers
6
+ License: MIT
7
+ Classifier: Programming Language :: Python :: 3
8
+ Classifier: Intended Audience :: Education
9
+ Classifier: Topic :: Education
10
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
11
+ Requires-Python: >=3.9
12
+ Description-Content-Type: text/markdown
13
+ License-File: LICENSE
14
+ Provides-Extra: student
15
+ Requires-Dist: pytest<10,>=8; extra == "student"
16
+ Provides-Extra: dev
17
+ Requires-Dist: pytest<10,>=8; extra == "dev"
18
+ Requires-Dist: build; extra == "dev"
19
+ Requires-Dist: twine; extra == "dev"
20
+ Dynamic: license-file
21
+
22
+ # šŸ¤– Robo – Classroom Programming Environment
23
+
24
+ A simple Python-based learning environment for teaching programming functions-first through grid-based robot challenges.
25
+
26
+ ## Setup
27
+
28
+ ### Install from PyPI
29
+
30
+ ```bash
31
+ pip install robo
32
+ ```
33
+
34
+ ### Development install
35
+
36
+ ```bash
37
+ git clone <repo>
38
+ cd robo
39
+ pip install -e .
40
+ ```
41
+
42
+ ## Run a world
43
+
44
+ From a student project folder:
45
+
46
+ ```bash
47
+ python -m robo world1.0
48
+ ```
49
+
50
+ Student solution files live in:
51
+
52
+ ```text
53
+ exercises/
54
+ world1_0.py
55
+ ```
56
+
57
+ ## Check progress
58
+
59
+ ```bash
60
+ python -m robo summary
61
+ ```
62
+
63
+ ## Run tests
64
+
65
+ From the project root:
66
+
67
+ ```bash
68
+ python -m pytest
69
+ ```
70
+
71
+ Run tests when you:
72
+
73
+ - add new worlds
74
+ - change commands
75
+ - change runner/display behaviour
76
+
77
+ ## Contribute
78
+
79
+ 1. Create or edit code.
80
+ 2. Run the test suite.
81
+ 3. Check that student worlds still run.
82
+ 4. Open a pull request.
83
+
84
+ See the `docs/` folder for pupil-facing command guides and teacher/developer notes.
@@ -0,0 +1,71 @@
1
+ LICENSE
2
+ README.md
3
+ pyproject.toml
4
+ src/pio_robo.egg-info/PKG-INFO
5
+ src/pio_robo.egg-info/SOURCES.txt
6
+ src/pio_robo.egg-info/dependency_links.txt
7
+ src/pio_robo.egg-info/entry_points.txt
8
+ src/pio_robo.egg-info/requires.txt
9
+ src/pio_robo.egg-info/top_level.txt
10
+ src/robo/__init__.py
11
+ src/robo/__main__.py
12
+ src/robo/cli.py
13
+ src/robo/commands.py
14
+ src/robo/project.py
15
+ src/robo/run.py
16
+ src/robo/world_tools.py
17
+ src/robo/solutions/__init__.py
18
+ src/robo/solutions/world1_0.py
19
+ src/robo/solutions/world1_1.py
20
+ src/robo/solutions/world1_2.py
21
+ src/robo/solutions/world1_3.py
22
+ src/robo/solutions/world1_4.py
23
+ src/robo/solutions/world1_5.py
24
+ src/robo/solutions/world1_6.py
25
+ src/robo/solutions/world1_7.py
26
+ src/robo/solutions/world1_8.py
27
+ src/robo/solutions/world1_9.py
28
+ src/robo/solutions/world2_0.py
29
+ src/robo/solutions/world2_1.py
30
+ src/robo/teacher_solutions/__init__.py
31
+ src/robo/teacher_solutions/world1_0.py
32
+ src/robo/teacher_solutions/world1_1.py
33
+ src/robo/teacher_solutions/world1_2.py
34
+ src/robo/teacher_solutions/world1_3.py
35
+ src/robo/teacher_solutions/world1_4.py
36
+ src/robo/teacher_solutions/world1_5.py
37
+ src/robo/teacher_solutions/world1_6.py
38
+ src/robo/teacher_solutions/world1_7.py
39
+ src/robo/teacher_solutions/world1_8.py
40
+ src/robo/teacher_solutions/world1_9.py
41
+ src/robo/teacher_solutions/world2_0.py
42
+ src/robo/teacher_solutions/world2_1.py
43
+ src/robo/templates/__init__.py
44
+ src/robo/templates/exercises/world1_0.py
45
+ src/robo/templates/exercises/world1_1.py
46
+ src/robo/templates/exercises/world1_2.py
47
+ src/robo/templates/exercises/world1_3.py
48
+ src/robo/templates/exercises/world1_4.py
49
+ src/robo/templates/exercises/world1_5.py
50
+ src/robo/templates/exercises/world1_6.py
51
+ src/robo/templates/exercises/world1_7.py
52
+ src/robo/templates/exercises/world1_8.py
53
+ src/robo/templates/exercises/world1_9.py
54
+ src/robo/templates/exercises/world2_0.py
55
+ src/robo/templates/exercises/world2_1.py
56
+ src/robo/worlds/__init__.py
57
+ src/robo/worlds/world1_0.py
58
+ src/robo/worlds/world1_1.py
59
+ src/robo/worlds/world1_2.py
60
+ src/robo/worlds/world1_3.py
61
+ src/robo/worlds/world1_4.py
62
+ src/robo/worlds/world1_5.py
63
+ src/robo/worlds/world1_6.py
64
+ src/robo/worlds/world1_7.py
65
+ src/robo/worlds/world1_8.py
66
+ src/robo/worlds/world1_9.py
67
+ src/robo/worlds/world2_0.py
68
+ src/robo/worlds/world2_1.py
69
+ tests/test_commands.py
70
+ tests/test_display.py
71
+ tests/test_runner.py
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ robo = robo.cli:main
@@ -0,0 +1,8 @@
1
+
2
+ [dev]
3
+ pytest<10,>=8
4
+ build
5
+ twine
6
+
7
+ [student]
8
+ pytest<10,>=8
@@ -0,0 +1 @@
1
+ # Robo package
@@ -0,0 +1,4 @@
1
+ from robo.cli import main
2
+
3
+ if __name__ == "__main__":
4
+ main()
@@ -0,0 +1,115 @@
1
+ import argparse
2
+ from pathlib import Path
3
+
4
+ from robo.project import create_project
5
+ from robo.run import run_world, show_summary
6
+ from robo.world_tools import (
7
+ create_world_from_template,
8
+ delete_world,
9
+ list_world_files,
10
+ )
11
+
12
+
13
+ def build_parser():
14
+ parser = argparse.ArgumentParser(
15
+ prog="robo",
16
+ description="Robo: a simple grid-based Python teaching tool."
17
+ )
18
+
19
+ subparsers = parser.add_subparsers(
20
+ dest="command",
21
+ required=True
22
+ )
23
+
24
+ new_parser = subparsers.add_parser(
25
+ "new",
26
+ help="Create a new student project."
27
+ )
28
+ new_parser.add_argument(
29
+ "project_name",
30
+ help="Name of the student project folder to create."
31
+ )
32
+
33
+ run_parser = subparsers.add_parser(
34
+ "run",
35
+ help="Run one world from the current student project."
36
+ )
37
+
38
+ run_parser.add_argument(
39
+ "world_name",
40
+ help="World to run, e.g. world1.0"
41
+ )
42
+
43
+ subparsers.add_parser(
44
+ "summary",
45
+ help="Show which worlds are completed in the current student project."
46
+ )
47
+
48
+ make_world_parser = subparsers.add_parser(
49
+ "make-world",
50
+ help="Create the next world from scripts/new_world.py."
51
+ )
52
+ make_world_parser.add_argument(
53
+ "--name",
54
+ help="Optional world name, e.g. world2.0. If omitted, the next number is used."
55
+ )
56
+ make_world_parser.add_argument(
57
+ "--overwrite",
58
+ action="store_true",
59
+ help="Overwrite files if this world already exists."
60
+ )
61
+
62
+ delete_world_parser = subparsers.add_parser(
63
+ "delete-world",
64
+ help="Delete generated files for a world."
65
+ )
66
+ delete_world_parser.add_argument(
67
+ "--name",
68
+ required=True,
69
+ help="World name to delete, e.g. world2.0"
70
+ )
71
+ delete_world_parser.add_argument(
72
+ "--dry-run",
73
+ action="store_true",
74
+ help="Show what would be deleted without deleting files."
75
+ )
76
+
77
+ subparsers.add_parser(
78
+ "list-worlds",
79
+ help="List worlds in the package."
80
+ )
81
+
82
+ return parser
83
+
84
+
85
+ def main():
86
+ parser = build_parser()
87
+ args = parser.parse_args()
88
+
89
+ if args.command == "new":
90
+ create_project(args.project_name)
91
+
92
+ elif args.command == "run":
93
+ run_world(args.world_name, Path.cwd())
94
+
95
+ elif args.command == "summary":
96
+ show_summary(Path.cwd())
97
+
98
+ elif args.command == "make-world":
99
+ create_world_from_template(
100
+ overwrite=args.overwrite,
101
+ world_name=args.name
102
+ )
103
+
104
+ elif args.command == "delete-world":
105
+ delete_world(
106
+ world_name=args.name,
107
+ dry_run=args.dry_run
108
+ )
109
+
110
+ elif args.command == "list-worlds":
111
+ list_world_files()
112
+
113
+
114
+ if __name__ == "__main__":
115
+ main()
@@ -0,0 +1,178 @@
1
+ world = None
2
+ robo = None
3
+ trail = []
4
+
5
+ def load_world(w, r):
6
+ global world, robo, trail
7
+ world = w
8
+ robo = r
9
+ trail = [(robo["x"], robo["y"])]
10
+
11
+ def move():
12
+ x = robo["x"]
13
+ y = robo["y"]
14
+ d = robo["direction"]
15
+
16
+ if d == "N":
17
+ new_x, new_y = x, y + 1
18
+ elif d == "E":
19
+ new_x, new_y = x + 1, y
20
+ elif d == "S":
21
+ new_x, new_y = x, y - 1
22
+ elif d == "W":
23
+ new_x, new_y = x - 1, y
24
+
25
+ if not (0 <= new_x < 5 and 0 <= new_y < 5):
26
+ raise Exception("🚧 Can't leave the grid!")
27
+
28
+ if world[new_y][new_x] in ["|", "-"]:
29
+ raise Exception("ā›” Hit a wall!")
30
+
31
+ robo["x"] = new_x
32
+ robo["y"] = new_y
33
+ trail.append((new_x, new_y))
34
+ print("āž”ļø move")
35
+
36
+ def turn_left():
37
+ directions = ["N", "W", "S", "E"]
38
+ robo["direction"] = directions[(directions.index(robo["direction"]) + 1) % 4]
39
+ print("ā†©ļø turn left")
40
+
41
+ def turn_right():
42
+ directions = ["N", "E", "S", "W"]
43
+ robo["direction"] = directions[(directions.index(robo["direction"]) + 1) % 4]
44
+ print("ā†Ŗļø turn right")
45
+
46
+ def get_star():
47
+ x, y = robo["x"], robo["y"]
48
+
49
+ if world[y][x] == "⭐":
50
+ world[y][x] = "."
51
+ print("⭐ picked up")
52
+ else:
53
+ raise Exception("āŒ No star here!")
54
+
55
+ def place_star():
56
+ x, y = robo["x"], robo["y"]
57
+
58
+ if world[y][x] == ".":
59
+ world[y][x] = "⭐"
60
+ print("⭐ placed")
61
+ else:
62
+ raise Exception("āŒ Can't place star here!")
63
+
64
+ def get_position_in_front():
65
+ x = robo["x"]
66
+ y = robo["y"]
67
+ d = robo["direction"]
68
+
69
+ if d == "N":
70
+ return x, y + 1
71
+ elif d == "E":
72
+ return x + 1, y
73
+ elif d == "S":
74
+ return x, y - 1
75
+ elif d == "W":
76
+ return x - 1, y
77
+
78
+ def is_inside_grid(x, y):
79
+ return 0 <= x < 5 and 0 <= y < 5
80
+
81
+ def wall_ahead():
82
+ x, y = get_position_in_front()
83
+
84
+ if not is_inside_grid(x, y):
85
+ return True
86
+
87
+ return world[y][x] in ["|", "-"]
88
+
89
+ def can_move():
90
+ return not wall_ahead()
91
+
92
+ def star_here():
93
+ x = robo["x"]
94
+ y = robo["y"]
95
+
96
+ return world[y][x] == "⭐"
97
+
98
+ def star_ahead():
99
+ x, y = get_position_in_front()
100
+
101
+ if not is_inside_grid(x, y):
102
+ return False
103
+
104
+ return world[y][x] == "⭐"
105
+
106
+ def facing_north():
107
+ return robo["direction"] == "N"
108
+
109
+ def facing_east():
110
+ return robo["direction"] == "E"
111
+
112
+ def facing_south():
113
+ return robo["direction"] == "S"
114
+
115
+ def facing_west():
116
+ return robo["direction"] == "W"
117
+
118
+ # Pupil-friendly aliases for grid directions
119
+
120
+ def facing_up():
121
+ return facing_north()
122
+
123
+ def facing_right():
124
+ return facing_east()
125
+
126
+ def facing_down():
127
+ return facing_south()
128
+
129
+ def facing_left():
130
+ return facing_west()
131
+
132
+ def show_world():
133
+ print("\nšŸŒ WORLD")
134
+
135
+ for y in reversed(range(5)):
136
+ row = ""
137
+ for x in range(5):
138
+
139
+ # Determine cell content
140
+ if x == robo["x"] and y == robo["y"]:
141
+ cell = "šŸ¤–"
142
+ else:
143
+ cell = world[y][x]
144
+
145
+ # Pad NON-emoji cells
146
+ if cell in [".", "|", "-"]:
147
+ cell = cell + " " # make it width ~2
148
+ # emoji left as-is
149
+
150
+ row += cell + " "
151
+
152
+ print(row)
153
+
154
+ print(f"\n🧭 Facing: {robo['direction']}\n")
155
+
156
+
157
+
158
+ def show_custom_world(world_data, robo_data):
159
+ print("\nšŸŒ TARGET")
160
+
161
+ for y in reversed(range(5)):
162
+ row = ""
163
+ for x in range(5):
164
+ if x == robo_data["x"] and y == robo_data["y"]:
165
+ cell = "šŸ¤–"
166
+ else:
167
+ cell = world_data[y][x]
168
+
169
+ if cell in [".", "|", "-"]:
170
+ cell = cell + " "
171
+
172
+ row += cell + " "
173
+
174
+ print(row)
175
+
176
+ print(f"\n🧭 Facing: {robo_data['direction']}\n")
177
+
178
+