arcadeturtle 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 (67) hide show
  1. arcadeturtle-0.1.0/.gitignore +8 -0
  2. arcadeturtle-0.1.0/LICENSE +21 -0
  3. arcadeturtle-0.1.0/PHASE10_PLAN.md +150 -0
  4. arcadeturtle-0.1.0/PKG-INFO +192 -0
  5. arcadeturtle-0.1.0/README.md +162 -0
  6. arcadeturtle-0.1.0/ROADMAP.md +156 -0
  7. arcadeturtle-0.1.0/arcadeturtle/__init__.py +33 -0
  8. arcadeturtle-0.1.0/arcadeturtle/_commands.py +45 -0
  9. arcadeturtle-0.1.0/arcadeturtle/_coords.py +46 -0
  10. arcadeturtle-0.1.0/arcadeturtle/_engine.py +285 -0
  11. arcadeturtle-0.1.0/arcadeturtle/_errors.py +128 -0
  12. arcadeturtle-0.1.0/arcadeturtle/_events.py +207 -0
  13. arcadeturtle-0.1.0/arcadeturtle/_renderer.py +284 -0
  14. arcadeturtle-0.1.0/arcadeturtle/_shapes.py +213 -0
  15. arcadeturtle-0.1.0/arcadeturtle/_state.py +68 -0
  16. arcadeturtle-0.1.0/arcadeturtle/_text_fa.py +24 -0
  17. arcadeturtle-0.1.0/arcadeturtle/_vec2d.py +49 -0
  18. arcadeturtle-0.1.0/arcadeturtle/_window.py +48 -0
  19. arcadeturtle-0.1.0/arcadeturtle/api.py +257 -0
  20. arcadeturtle-0.1.0/arcadeturtle/assets/fonts/Vazirmatn-Regular.ttf +0 -0
  21. arcadeturtle-0.1.0/arcadeturtle/screen.py +264 -0
  22. arcadeturtle-0.1.0/arcadeturtle/turtle_obj.py +886 -0
  23. arcadeturtle-0.1.0/docs/screenshot.png +0 -0
  24. arcadeturtle-0.1.0/examples/00_healthcheck.py +51 -0
  25. arcadeturtle-0.1.0/examples/01_square.py +14 -0
  26. arcadeturtle-0.1.0/examples/02_star.py +16 -0
  27. arcadeturtle-0.1.0/examples/03_polygon_function.py +35 -0
  28. arcadeturtle-0.1.0/examples/04_color_spiral.py +18 -0
  29. arcadeturtle-0.1.0/examples/05_filled_shapes.py +42 -0
  30. arcadeturtle-0.1.0/examples/06_keyboard_drive.py +45 -0
  31. arcadeturtle-0.1.0/examples/07_click_paint.py +23 -0
  32. arcadeturtle-0.1.0/examples/08_snake_game.py +170 -0
  33. arcadeturtle-0.1.0/examples/09_compound_shape_art.py +38 -0
  34. arcadeturtle-0.1.0/examples/10_etch_a_sketch.py +31 -0
  35. arcadeturtle-0.1.0/examples/11_undo_drawing.py +51 -0
  36. arcadeturtle-0.1.0/examples/12_world_coordinates_chart.py +42 -0
  37. arcadeturtle-0.1.0/examples/13_pong.py +115 -0
  38. arcadeturtle-0.1.0/examples/14_space_invaders.py +123 -0
  39. arcadeturtle-0.1.0/examples/golden_snake_original.py +159 -0
  40. arcadeturtle-0.1.0/pyproject.toml +45 -0
  41. arcadeturtle-0.1.0/tests/manual/README.md +45 -0
  42. arcadeturtle-0.1.0/tests/manual/drive_etch_a_sketch.py +46 -0
  43. arcadeturtle-0.1.0/tests/manual/drive_golden_snake.py +59 -0
  44. arcadeturtle-0.1.0/tests/manual/drive_pong.py +74 -0
  45. arcadeturtle-0.1.0/tests/manual/drive_snake.py +63 -0
  46. arcadeturtle-0.1.0/tests/manual/drive_space_invaders.py +66 -0
  47. arcadeturtle-0.1.0/tests/manual/drive_undo_drawing.py +48 -0
  48. arcadeturtle-0.1.0/tests/manual/run_and_close.py +26 -0
  49. arcadeturtle-0.1.0/tests/manual/run_example_shot.py +36 -0
  50. arcadeturtle-0.1.0/tests/manual/verify_drag_visual.py +57 -0
  51. arcadeturtle-0.1.0/tests/manual/verify_shapes_visual.py +67 -0
  52. arcadeturtle-0.1.0/tests/manual/verify_world_bgpic_visual.py +56 -0
  53. arcadeturtle-0.1.0/tests/test_commands.py +20 -0
  54. arcadeturtle-0.1.0/tests/test_coords.py +16 -0
  55. arcadeturtle-0.1.0/tests/test_drawing.py +322 -0
  56. arcadeturtle-0.1.0/tests/test_engine.py +128 -0
  57. arcadeturtle-0.1.0/tests/test_errors.py +145 -0
  58. arcadeturtle-0.1.0/tests/test_events.py +208 -0
  59. arcadeturtle-0.1.0/tests/test_examples_smoke.py +65 -0
  60. arcadeturtle-0.1.0/tests/test_poly_clone_units.py +211 -0
  61. arcadeturtle-0.1.0/tests/test_screen.py +231 -0
  62. arcadeturtle-0.1.0/tests/test_shapes.py +277 -0
  63. arcadeturtle-0.1.0/tests/test_state.py +57 -0
  64. arcadeturtle-0.1.0/tests/test_undo.py +223 -0
  65. arcadeturtle-0.1.0/tests/test_vec2d.py +58 -0
  66. arcadeturtle-0.1.0/tests/test_window_wiring.py +102 -0
  67. arcadeturtle-0.1.0/tests/test_world_and_mode.py +180 -0
@@ -0,0 +1,8 @@
1
+ __pycache__/
2
+ *.py[cod]
3
+ .venv/
4
+ *.egg-info/
5
+ dist/
6
+ build/
7
+ .pytest_cache/
8
+ tests/manual/_out_*.png
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 zack-riftwalker
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,150 @@
1
+ # فاز ۱۰ (گسترش‌یافته) — سازگاری کامل با turtle استاندارد
2
+
3
+ هدف: **هر اسکریپت turtle واقعی روی اینترنت، فقط با تغییر خط import، روی ArcadeTurtle اجرا شود** — نه فقط بازی مار، بلکه Space Invaders، Pong، Etch-a-Sketch، نقاشی‌های compound-shape و اسکریپت‌های کلاس درس.
4
+
5
+ مبنای این پلن حدس نیست: خروجی مقایسه‌ی مستقیم `dir()` بین `turtle` استاندارد و `arcadeturtle` است:
6
+
7
+ | سطح | stdlib | ما داریم | جا مانده |
8
+ |---|---|---|---|
9
+ | توابع سطح ماژول | ۱۲۲ | ۷۳ | **۴۹** |
10
+ | متدهای Turtle | ۸۴ | ۵۶ | **۲۸** |
11
+ | متدهای Screen | — | — | **۷** |
12
+
13
+ موارد جامانده در ۸ زیرفاز دسته‌بندی شده‌اند، به ترتیب «بیشترین استفاده در آموزش‌های واقعی، اول».
14
+
15
+ ---
16
+
17
+ ## ۱۰.۱ — بردهای آسان: توابع سطح ماژول که فقط باید وصل شوند
18
+
19
+ این‌ها از قبل روی `Screen()` یا `Turtle` ما وجود دارند ولی در سطح ماژول (سبک `import arcadeturtle as turtle; turtle.setup(...)`) در دسترس نیستند. فقط forwarding لازم است:
20
+
21
+ ```
22
+ setup, window_width, window_height, screensize, bye, exitonclick,
23
+ clearscreen, resetscreen, bgpic*, addshape*, register_shape*,
24
+ setworldcoordinates*, mode*, delay*, turtles*
25
+ ```
26
+ (ستاره‌دارها فعلاً خطای «پشتیبانی نمی‌شود» می‌دهند — همان خطا را در سطح ماژول هم بدهند تا بعداً که پیاده شدند خودکار وصل باشند.)
27
+
28
+ همچنین aliasهای کلاس برای اسکریپت‌هایی که مستقیم استفاده می‌کنند:
29
+ - `Pen = Turtle` و `RawTurtle = Turtle` و `RawPen = Turtle` (در stdlib هم `Pen` همان `Turtle` است)
30
+ - `TurtleScreen`/`ScrolledCanvas`/`getcanvas` → خطای مهربان «مخصوص Tkinter است» (این‌ها فقط در اسکریپت‌های embedding استفاده می‌شوند)
31
+ - استثنای `Terminator` (وقتی پنجره بسته می‌شود ولی اسکریپت هنوز دستور می‌دهد — الان چه می‌کنیم؟ باید همین استثنا را بدهیم)
32
+
33
+ **سختی: کم. اثر: فوری — ده‌ها اسکریپت فقط به‌خاطر همین‌ها می‌شکنند.**
34
+
35
+ ## ۱۰.۲ — Vec2D و کوئری‌های برداری
36
+
37
+ در stdlib، `position()`/`pos()` یک **`Vec2D`** برمی‌گرداند نه تاپل ساده — و آموزش‌های فیزیک/بازی رویش جمع و ضرب برداری می‌کنند: `t.goto(t.pos() + Vec2D(0, 10))`. الان تاپل برمی‌گردانیم و آن اسکریپت‌ها می‌ترکند.
38
+
39
+ - کلاس `Vec2D(tuple)` با `+`, `-`, `*` (اسکالر و داخلی)، `abs`, `rotate` — پیاده‌سازی stdlib کوچک است و می‌شود همان را الگو گرفت
40
+ - `position()`, `pos()`, `towards`/`distance` ورودی Vec2D بپذیرند
41
+ - `teleport(x, y, fill_gap=False)` (اضافه‌شده در پایتون ۳.۱۲): مثل goto ولی بدون خط و بدون انیمیشن، حتی با قلم پایین
42
+
43
+ **سختی: کم.**
44
+
45
+ ## ۱۰.۳ — شکل‌ها: بزرگ‌ترین شکاف واقعی
46
+
47
+ پرتکرارترین دلیل شکستن آموزش‌های بازی (Space Invaders، مسابقه ماشین، …):
48
+
49
+ - **`register_shape(name, polygon)`** — شکل چندضلعی سفارشی
50
+ - **`Shape` class + compound shapes** — `Shape("compound")` با `addcomponent(poly, fill, outline)`؛ در نقاشی‌های پیشرفته رایج است
51
+ - **`register_shape("image.gif")`** — شکل تصویری. Arcade خودش Sprite/Texture دارد؛ باید GIF *و* PNG را پشتیبانی کنیم (PNG مزیت ماست، GIF برای سازگاری)
52
+ - `getshapes()` — فهرست شکل‌های ثبت‌شده
53
+ - `get_shapepoly()` — چندضلعی شکل فعلی
54
+ - **`resizemode("auto"|"user"|"noresize")`** — در stdlib با `pensize` یا `shapesize` شکل بزرگ می‌شود؛ خیلی از بازی‌ها `shapesize` با `resizemode` می‌زنند
55
+ - `tilt(angle)`, `tiltangle()`, `settiltangle()` — چرخش ظاهر بدون تغییر جهت حرکت (در انیمیشن چرخ ماشین و سفینه استفاده می‌شود)
56
+ - `shearfactor()`, `shapetransform()` — کم‌استفاده ولی برای «کامل بودن» لازم؛ ماتریس ۲×۲ روی چندضلعی کرسر
57
+
58
+ نکته معماری: `_renderer.py` الان کرسر را از `SHAPE_POLYGONS` ثابت می‌کشد. باید یک **رجیستری شکل per-engine** جایگزین شود که سه نوع دارد: polygon، compound، image. مسیر رسم image از مسیر polygon جداست (arcade.Texture + draw)؛ تصویر باید با `tracer(0)` هم درست کار کند.
59
+
60
+ **سختی: متوسط تا زیاد. اثر: بالاترین — این زیرفاز بیشترین اسکریپت‌ها را زنده می‌کند.**
61
+
62
+ ## ۱۰.۴ — undo
63
+
64
+ در آموزش کودکان و محیط‌های تعاملی زیاد استفاده می‌شود:
65
+
66
+ - هر عمل قابل‌بازگشت (حرکت، چرخش، رنگ، write، stamp، fill) یک entry در **بافر undo مخصوص همان لاک‌پشت** بگذارد
67
+ - `undo()` آخرین entry را برگرداند: state منطقی + حذف از renderer (خط/متن/مهر با شناسه‌ی خودش پاک شود — زیرساخت `turtle_id` که در فاز رفع باگ‌ها ساختیم دقیقاً همین‌جا به کار می‌آید)
68
+ - `setundobuffer(size)` و `undobufferentries()`؛ پیش‌فرض stdlib بافر ۱۰۰۰تایی است؛ `None` یعنی خاموش
69
+ - تعامل با انیمیشن: undo روی فرمانی که هنوز در صف است باید آن را از صف بردارد، نه اینکه اجرا و بعد برعکس کند
70
+
71
+ **سختی: متوسط. ریسک: تعامل صف/undo — تست دقیق لازم دارد.**
72
+
73
+ ## ۱۰.۵ — رویدادهای باقی‌مانده
74
+
75
+ - **`Turtle.onclick(fn)`** — کلیک *روی خود لاک‌پشت* (نه صفحه). الان فقط کلیک صفحه داریم. لازمه‌اش hit-test روی چندضلعی/تصویر کرسر است
76
+ - **`Turtle.ondrag(fn)`** — کشیدن لاک‌پشت با موس؛ پایه‌ی آموزش محبوب «Etch-A-Sketch» و اسباب‌بازی‌های تعاملی. نیازمند `on_mouse_drag` در `_window.py`
77
+ - `Turtle.onrelease(fn)` — رهاکردن موس روی لاک‌پشت
78
+ - `Screen.delay(ms)` — تأخیر بین به‌روزرسانی‌ها؛ باید روی سرعت پخش صف اثر بگذارد (نگاشت به مدل انیمیشن ما: یک ضریب سراسری)
79
+ - معنای دقیق `tracer(n)` برای n>1: هر n اقدام یک به‌روزرسانی — الان فقط 0 و 1 را درست انجام می‌دهیم
80
+
81
+ **سختی: متوسط.**
82
+
83
+ ## ۱۰.۶ — چندضلعی‌سازی و clone
84
+
85
+ - `begin_poly()`, `end_poly()`, `get_poly()` — ضبط مسیر حرکت به‌عنوان چندضلعی؛ همراه `register_shape` برای ساخت شکل سفارشی «با خود لاک‌پشت» استفاده می‌شود
86
+ - **`clone()`** — کپی کامل لاک‌پشت (state + شکل + قلم)؛ در بعضی نسخه‌های بازی مار برای بدنه استفاده می‌شود
87
+ - `getturtle()`/`getpen()` — خودِ شیء را برمی‌گرداند (یک خط)
88
+ - `pen()` — گرفتن/ست‌کردن دسته‌ای همه ویژگی‌های قلم به‌صورت dict؛ اسکریپت‌های پیشرفته برای ذخیره/بازیابی وضعیت استفاده می‌کنند
89
+ - `degrees(fullcircle)`, `radians()` — تغییر واحد زاویه؛ روی همه ورودی/خروجی‌های زاویه اثر می‌گذارد (`left/right/heading/setheading/towards`)
90
+
91
+ **سختی: کم تا متوسط. `degrees/radians` باید مرکزی در `TurtleState` حل شود نه پراکنده.**
92
+
93
+ ## ۱۰.۷ — دیالوگ‌های ورودی و پس‌زمینه تصویری
94
+
95
+ - **`textinput(title, prompt)`** و **`numinput(title, prompt, default, minval, maxval)`** — چون Arcade دیالوگ آماده ندارد، ساده‌ترین راهِ سازگار: `tkinter.simpledialog` (در همه نصب‌های استاندارد پایتون هست و لازم نیست پنجره turtle مال Tk باشد). نسخه فارسی‌پسند: عنوان/پیام فارسی درست رندر شود
96
+ - **`bgpic("image.png"|"nopic")`** — تصویر پس‌زمینه، وسط‌چین مثل stdlib؛ GIF و PNG
97
+ - `Screen.mode("standard"|"logo"|"world")` — حالت logo (زاویه صفر = شمال، ساعتگرد) در آموزش‌های مدرسه‌ای مکرر است؛ باید در `_coords.py`/`TurtleState` به‌صورت متمرکز اعمال شود
98
+ - **`setworldcoordinates(llx, lly, urx, ury)`** — مختصات دلخواه کاربر؛ چون تبدیل مختصات ما متمرکز در `_coords.py` است، این فقط یک لایه‌ی scale/offset روی همان است — معماری فعلی دقیقاً برای همین ساخته شد
99
+
100
+ **سختی: متوسط. `mode('logo')` و world باید با هم طراحی شوند چون هر دو تبدیل مختصات/زاویه‌اند.**
101
+
102
+ ## ۱۰.۸ — تمیزکاری نهایی سازگاری
103
+
104
+ - `Screen.turtles()` و `turtles()` سطح ماژول — فهرست لاک‌پشت‌ها (WeakSet فاز قبل را داریم)
105
+ - بازبینی امضای همه توابع موجود در برابر stdlib (آرگومان‌های اختیاری، مقادیر برگشتی — مثلاً `pencolor()` بدون آرگومان در stdlib رشته یا تاپل برمی‌گرداند بسته به نحوه‌ی ست‌شدن؛ ما همین کار را می‌کنیم؟ تست شود)
106
+ - `write_docstringdict` → no-op بی‌صدا (فقط برای ترجمه docstring در stdlib است)
107
+ - هر چیزی که آگاهانه پیاده نمی‌شود (getcanvas، ScrolledCanvas) خطای مهربانِ «چرا» بدهد
108
+ - **به‌روزرسانی کامل جدول سازگاری README** — بعد از این فاز، ستون ❌ باید تقریباً خالی باشد
109
+
110
+ ---
111
+
112
+ ## آزمون‌های طلایی جدید (معیار پایان فاز ۱۰)
113
+
114
+ مثل «آزمون طلایی مار»، این ۶ اسکریپت از آموزش‌های واقعی و محبوب اینترنت، فقط با تغییر import باید اجرا شوند:
115
+
116
+ 1. **Space Invaders** (سبک Real Python) — `register_shape` تصویری، چند لاک‌پشت، کیبورد، برخورد
117
+ 2. **Pong** — دو راکت، توپ، `ontimer`/حلقه، امتیاز
118
+ 3. **Etch-A-Sketch** — `ondrag` و `onclick` روی لاک‌پشت
119
+ 4. **نقاشی compound-shape** — `Shape("compound")` + `addcomponent`
120
+ 5. **اسکریپت undo تعاملی** — نقاشی با کیبورد + دکمه undo
121
+ 6. **اسکریپت مختصات جهانی** — `setworldcoordinates` + رسم نمودار ساده
122
+
123
+ هرکدام: یک اسکریپت راننده‌ی خودکار در `tests/manual/` + تست دود compile در `tests/test_examples_smoke.py`.
124
+
125
+ ## ترتیب اجرا و منطق
126
+
127
+ ```
128
+ ۱۰.۱ (وصل‌کردن‌ها — یک روز، اثر فوری)
129
+ → ۱۰.۲ (Vec2D — پایه‌ی بقیه چون position همه‌جا استفاده می‌شود)
130
+ → ۱۰.۳ (شکل‌ها — بزرگ‌ترین برد)
131
+ → ۱۰.۵ (رویدادها — بعدش Etch-A-Sketch و Space Invaders تست می‌شوند)
132
+ → ۱۰.۴ (undo — مستقل، ولی بعد از شکل‌ها تا undo مهر/تصویر هم پوشش بگیرد)
133
+ → ۱۰.۶ (poly/clone/degrees)
134
+ → ۱۰.۷ (دیالوگ‌ها، bgpic، world/logo)
135
+ → ۱۰.۸ (تمیزکاری + README + آزمون‌های طلایی نهایی)
136
+ ```
137
+
138
+ هر زیرفاز: پیاده‌سازی → تست‌های headless → تأیید دستی با پنجره → کامیت جدا.
139
+
140
+ ## ریسک‌های اصلی
141
+
142
+ | ریسک | مقابله |
143
+ |---|---|
144
+ | تصاویر (shape/bgpic) با معماری «بافر پخته» ما تداخل کنند | تصویر جدا از ShapeElementList رندر می‌شود؛ از همان الگوی متن (arcade.Text) پیروی کند |
145
+ | undo × صف انیمیشن حالت‌های لبه‌ای بسازد | undo اول صف را خالی می‌کند (مثل tracer(0)) بعد برمی‌گرداند — ساده و قابل پیش‌بینی |
146
+ | mode('logo') همه محاسبات زاویه را بشکند | تبدیل زاویه فقط در TurtleState/_coords متمرکز بماند؛ تست مقایسه‌ای مستقیم با stdlib برای هر ۴ حالت |
147
+ | tkinter برای دیالوگ‌ها در بعضی محیط‌ها نباشد | try/except با خطای مهربان + مستندسازی |
148
+ | حجم زیاد → فرسایش | هر زیرفاز خروجی مستقل قابل تست دارد؛ بعد از ۱۰.۳ هر لحظه می‌شود متوقف شد و منتشر کرد |
149
+
150
+ بعد از پایان فاز ۱۰ → فاز ۹ (CI، PyPI، انتشار) طبق تصمیم قبلی.
@@ -0,0 +1,192 @@
1
+ Metadata-Version: 2.4
2
+ Name: arcadeturtle
3
+ Version: 0.1.0
4
+ Summary: کتابخانه آموزشی لاک‌پشت با موتور Arcade — همان API ماژول turtle، با گرافیک مدرن و پشتیبانی فارسی
5
+ Project-URL: Homepage, https://github.com/zack-riftwalker/arcadeturtle
6
+ Project-URL: Repository, https://github.com/zack-riftwalker/arcadeturtle
7
+ Project-URL: Issues, https://github.com/zack-riftwalker/arcadeturtle/issues
8
+ Author: zack-riftwalker
9
+ License-Expression: MIT
10
+ License-File: LICENSE
11
+ Keywords: arcade,education,farsi,graphics,persian,turtle
12
+ Classifier: Development Status :: 4 - Beta
13
+ Classifier: Intended Audience :: Education
14
+ Classifier: Operating System :: OS Independent
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.10
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Programming Language :: Python :: 3.13
20
+ Classifier: Topic :: Education
21
+ Classifier: Topic :: Multimedia :: Graphics
22
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
23
+ Requires-Python: >=3.10
24
+ Requires-Dist: arabic-reshaper>=3.0
25
+ Requires-Dist: arcade<4,>=3.0
26
+ Requires-Dist: python-bidi>=0.4
27
+ Provides-Extra: dev
28
+ Requires-Dist: pytest>=8.0; extra == 'dev'
29
+ Description-Content-Type: text/markdown
30
+
31
+ # ArcadeTurtle 🐢
32
+
33
+ کتابخانه‌ای آموزشی: همان دستورهای ماژول استاندارد `turtle` پایتون، اما با
34
+ موتور گرافیکی مدرن **[Arcade](https://api.arcade.academy/)** — حرکت نرم،
35
+ پنجره باکیفیت، و پشتیبانی کامل از متن فارسی در `write()`.
36
+
37
+ ![نمونه خروجی ArcadeTurtle](docs/screenshot.png)
38
+
39
+ ## نصب
40
+
41
+ ```bash
42
+ python -m venv .venv
43
+ .venv\Scripts\activate # ویندوز
44
+ pip install -e .
45
+ ```
46
+
47
+ ## شروع سریع
48
+
49
+ ```python
50
+ import arcadeturtle as turtle
51
+
52
+ for _ in range(4):
53
+ turtle.forward(100)
54
+ turtle.left(90)
55
+
56
+ turtle.done()
57
+ ```
58
+
59
+ همین! هر برنامه‌ای که با ماژول `turtle` استاندارد نوشته باشی، معمولاً فقط با
60
+ تغییر همین یک خط `import` روی ArcadeTurtle هم اجرا می‌شود.
61
+
62
+ پوشش API در سطح ماژول ۱۰۰٪ است (هر نام در `turtle.__all__` استاندارد این‌جا
63
+ هم وجود دارد). برای اثبات این ادعا، هفت «آزمون طلایی» در پوشه‌ی
64
+ [`examples/`](examples/) هستند: اسکریپت‌هایی به سبک رایج آموزش‌های اینترنتی
65
+ (بازی مار، پونگ، فضاپیمای مهاجم، Etch-A-Sketch، شکل ترکیبی، نقاشی با undo،
66
+ نمودار با مختصات جهانی) که فقط با تغییر `import` روی ArcadeTurtle اجرا
67
+ می‌شوند.
68
+
69
+ ## مثال‌ها
70
+
71
+ مثال‌ها در پوشه‌ی [`examples/`](examples/) از ساده به پیشرفته مرتب‌اند:
72
+
73
+ | فایل | چه چیزی یاد می‌دهد |
74
+ |---|---|
75
+ | [`00_healthcheck.py`](examples/00_healthcheck.py) | آیا Arcade و متن فارسی روی سیستم تو کار می‌کنند؟ |
76
+ | [`01_square.py`](examples/01_square.py) | اولین حرکت، چرخش و حلقه |
77
+ | [`02_star.py`](examples/02_star.py) | ستاره پنج‌پر، رنگ پس‌زمینه |
78
+ | [`03_polygon_function.py`](examples/03_polygon_function.py) | نوشتن تابع با پارامتر |
79
+ | [`04_color_spiral.py`](examples/04_color_spiral.py) | حلقه‌های تودرتو، `speed(0)` |
80
+ | [`05_filled_shapes.py`](examples/05_filled_shapes.py) | `begin_fill`/`end_fill`، `dot`، نوشتن فارسی |
81
+ | [`06_keyboard_drive.py`](examples/06_keyboard_drive.py) | کنترل با کیبورد (`onkeypress`) |
82
+ | [`07_click_paint.py`](examples/07_click_paint.py) | رویداد کلیک موس (`onscreenclick`) |
83
+ | [`08_snake_game.py`](examples/08_snake_game.py) | بازی کامل مار، چند لاک‌پشت، تابلوی امتیاز فارسی |
84
+ | [`golden_snake_original.py`](examples/golden_snake_original.py) | یک اسکریپت مار به سبک رایج اینترنتی — فقط با تغییر `import` اجرا می‌شود |
85
+ | [`09_compound_shape_art.py`](examples/09_compound_shape_art.py) | `Shape("compound")`، `addcomponent`، `stamp` |
86
+ | [`10_etch_a_sketch.py`](examples/10_etch_a_sketch.py) | `Turtle.ondrag` — کشیدن با موس |
87
+ | [`11_undo_drawing.py`](examples/11_undo_drawing.py) | نقاشی با کیبورد + `undo()` |
88
+ | [`12_world_coordinates_chart.py`](examples/12_world_coordinates_chart.py) | `setworldcoordinates` — رسم نمودار در واحدهای داده |
89
+ | [`13_pong.py`](examples/13_pong.py) | بازی دونفره، `ontimer`، برخورد |
90
+ | [`14_space_invaders.py`](examples/14_space_invaders.py) | `register_shape` با فایل تصویری، برخورد گلوله/دشمن |
91
+
92
+ ## جدول سازگاری با ماژول `turtle` استاندارد
93
+
94
+ ### ✅ کامل پشتیبانی می‌شود
95
+
96
+ حرکت: `forward`/`fd`، `backward`/`bk`/`back`، `left`/`lt`، `right`/`rt`،
97
+ `goto`/`setpos`/`setposition`، `setx`، `sety`، `home`، `setheading`/`seth` •
98
+ قلم: `penup`/`pu`/`up`، `pendown`/`pd`/`down`، `isdown`، `pensize`/`width`،
99
+ `pencolor`، `speed` • ترسیم: `circle`، `dot`، `stamp`، `clearstamp`،
100
+ `clearstamps`، `begin_fill`، `end_fill`، `filling`، `fillcolor`، `color`،
101
+ `write` (با پشتیبانی فارسی خودکار) • ظاهر: `shape`، `shapesize`/`turtlesize`،
102
+ `resizemode`، `tilt`، `tiltangle`، `settiltangle`، `shearfactor`،
103
+ `shapetransform`، `get_shapepoly`، `hideturtle`/`ht`، `showturtle`/`st`،
104
+ `isvisible` • شکل‌های سفارشی: `register_shape`/`addshape` (چندضلعی، شکل
105
+ ترکیبی compound، یا فایل تصویری PNG/GIF)، `Shape` class با `addcomponent`،
106
+ `getshapes` • کوئری: `position`/`pos` (به‌صورت `Vec2D`)، `xcor`، `ycor`،
107
+ `heading`، `towards`، `distance`، `teleport`، `degrees`، `radians` (واحد
108
+ اندازه‌گیری زاویه، جداگانه برای هر لاک‌پشت) • رویداد: `listen`، `onkey`،
109
+ `onkeypress`، `onkeyrelease`، `onclick`/`onscreenclick`، `ontimer`، `tracer`،
110
+ `update`، `Turtle.onclick`/`ondrag`/`onrelease` (کلیک/کشیدن روی خودِ
111
+ لاک‌پشت) • برگشت: `undo`، `setundobuffer`، `undobufferentries` (بافر
112
+ جداگانه برای هر لاک‌پشت) • ابزار: `pen` (گرفتن/ست‌کردن دسته‌ای وضعیت قلم)،
113
+ `getturtle`/`getpen`، `clone`، `begin_poly`/`end_poly`/`get_poly` (ضبط
114
+ چندضلعی دلخواه برای `register_shape` بعدی) • صفحه: `bgcolor`، `title`،
115
+ `colormode`، `mode` (`standard`/`logo`/`world`)، `setworldcoordinates`،
116
+ `bgpic`، `textinput`، `numinput`، `clear`، `reset`، `done`/`mainloop` •
117
+ کلاس‌ها: `Turtle()`/`Pen()`/`RawTurtle()`/`RawPen()` (چند نمونه مستقل)،
118
+ `Screen()` (سینگلتون) با `setup`، `window_width`/`window_height`،
119
+ `exitonclick`، `bye`، `Vec2D`.
120
+
121
+ `clear`/`reset` دقیقاً مثل turtle اصلی تفکیک شده‌اند: `Turtle.clear()` فقط
122
+ نقاشی‌های همان لاک‌پشت را پاک می‌کند، `Screen.clear()` همه‌چیز را، و
123
+ `Screen.reset()` علاوه بر آن همه لاک‌پشت‌ها را به حالت اولیه برمی‌گرداند.
124
+
125
+ ### 🟡 رفتار کمی متفاوت
126
+
127
+ - **`speed`**: عدد بین ۰ تا ۱۰ (یا نام‌های `fastest`/`fast`/`normal`/`slow`/
128
+ `slowest`)؛ سرعت واقعی پیکسل بر ثانیه با turtle اصلی یکی نیست، ولی معنای
129
+ نسبی (بزرگ‌تر = سریع‌تر، ۰ = آنی) همان است.
130
+ - **`colormode`**: مثل turtle اصلی کار می‌کند (پیش‌فرض ۲۵۵؛ برای رنگ‌های
131
+ اعشاری اول `colormode(1.0)` را صدا بزن) — ولی برخلاف turtle اصلی، حالت رنگ
132
+ سراسری است و به یک شیء Screen خاص گره نخورده.
133
+ - **شکل تصویری می‌چرخد** — برخلاف turtle اصلی که صراحتاً می‌گوید «شکل‌های
134
+ تصویری با چرخیدن لاک‌پشت نمی‌چرخند»، این‌جا عمداً برعکس تصمیم گرفته شده:
135
+ تصویر با heading می‌چرخد، چون هدف پشتیبانی از بازی‌های سبک Space Invaders/
136
+ مسابقه‌ای است که انتظار چرخش دارند. اگر تصویرت را با «رو به شرق» طراحی کنی
137
+ (نوکش رو به راست)، در heading=0 دقیقاً هم‌جهت با شکل‌های چندضلعی می‌ایستد.
138
+ - **`shearfactor`/`shapetransform`**: ساده‌سازی‌شده نسبت به فرمول داخلی
139
+ turtle اصلی؛ نتیجه‌ی بصری مشابه است ولی از نظر عددی («تجزیه»ی دقیق ماتریس)
140
+ یکی نیست. برای اکثر اسکریپت‌های آموزشی که این‌ها را جداگانه صدا می‌زنند
141
+ فرقی احساس نمی‌شود.
142
+ - **`resizemode("auto")`**: اندازه‌ی شکل برابر `pensize/5` محاسبه می‌شود —
143
+ یک تقریب مستند، نه فرمول دقیق stdlib.
144
+ - **`Turtle.onclick`/`ondrag`/`onrelease`**: تشخیص «روی لاک‌پشت» با یک دایره‌ی
145
+ احاطه‌کننده است، نه برخورد دقیق نقطه-در-چندضلعی — برای تعامل معمولی
146
+ (مثل کشیدن با موس) کافی است.
147
+ - **`undo`**: بافر جداگانه برای هر لاک‌پشت (نه یک بافر مشترک سراسری)،
148
+ و به‌جای بازسازی دقیق مثل stdlib، هر رکورد آنی state قبلی را برمی‌گرداند.
149
+ همان دستورهایی که stdlib هم undo می‌کند پوشش داده شده: حرکت، چرخش، مهر،
150
+ نوشته، `dot`، `begin_fill`/`end_fill`، و ویژگی‌های قلم. تغییرات ظاهری مثل
151
+ `shape`/`tilt`/`resizemode` در stdlib هم قابل undo نیستند.
152
+ - **`bgpic`**: تصویر پس‌زمینه با اندازه‌ی طبیعی خودش وسط‌چین می‌شود (مثل
153
+ stdlib) — کش داده نمی‌شود که کل پنجره را پر کند.
154
+ - **`textinput`/`numinput`**: با `tkinter.simpledialog` پیاده شده‌اند (چون
155
+ Arcade دیالوگ ورودی آماده ندارد)؛ یک پنجره‌ی جدا از پنجره‌ی اصلی باز
156
+ می‌شود. اگر tkinter روی سیستم در دسترس نباشد، خطای مهربان می‌دهند.
157
+
158
+ ### ❌ هنوز پشتیبانی نمی‌شود
159
+
160
+ `getscreen()` چندپنجره‌ای (فقط یک پنجره در هر زمان)، و `write(move=True)`.
161
+
162
+ این‌ها به‌صراحت خطای فارسی مهربان می‌دهند، نه رفتار خاموش یا نادرست.
163
+
164
+ دلیل `write(move=True)`: برای جابه‌جا کردن قلم به انتهای متن باید عرض متن
165
+ اندازه‌گیری شود که به پنجره‌ی باز نیاز دارد، ولی `write()` معمولاً قبل از
166
+ `done()` صدا زده می‌شود. به‌جایش خودت قلم را جابه‌جا کن:
167
+
168
+ ```python
169
+ penup()
170
+ write("سلام")
171
+ goto(xcor() + 60, ycor())
172
+ pendown()
173
+ ```
174
+
175
+ ## پشت صحنه چطور کار می‌کند؟
176
+
177
+ سه تصمیم معماری کلیدی (شرح کامل در [ROADMAP.md](ROADMAP.md)):
178
+
179
+ 1. **صف فرمان واحد**: هر فراخوانی دستور یک «فرمان» به یک صف FIFO مشترک
180
+ اضافه می‌کند. وضعیت منطقی لاک‌پشت (برای `position()` و مانند آن) بلافاصله
181
+ به‌روز می‌شود؛ پخش انیمیشنِ آن روی صفحه در فریم‌های بعدی انجام می‌شود.
182
+ 2. **تبدیل مختصات متمرکز**: تنها در `_coords.py` — مبدأ Turtle وسط صفحه،
183
+ مبدأ Arcade گوشه پایین-چپ.
184
+ 3. **بافر خط پخته‌شده**: خط‌های تمام‌شده یک‌بار در `ShapeElementList` Arcade
185
+ ساخته می‌شوند تا نقاشی‌های پیچیده (مثل مارپیچ) کند نشوند.
186
+
187
+ ## اجرای تست‌ها
188
+
189
+ ```bash
190
+ pip install -e ".[dev]"
191
+ pytest
192
+ ```
@@ -0,0 +1,162 @@
1
+ # ArcadeTurtle 🐢
2
+
3
+ کتابخانه‌ای آموزشی: همان دستورهای ماژول استاندارد `turtle` پایتون، اما با
4
+ موتور گرافیکی مدرن **[Arcade](https://api.arcade.academy/)** — حرکت نرم،
5
+ پنجره باکیفیت، و پشتیبانی کامل از متن فارسی در `write()`.
6
+
7
+ ![نمونه خروجی ArcadeTurtle](docs/screenshot.png)
8
+
9
+ ## نصب
10
+
11
+ ```bash
12
+ python -m venv .venv
13
+ .venv\Scripts\activate # ویندوز
14
+ pip install -e .
15
+ ```
16
+
17
+ ## شروع سریع
18
+
19
+ ```python
20
+ import arcadeturtle as turtle
21
+
22
+ for _ in range(4):
23
+ turtle.forward(100)
24
+ turtle.left(90)
25
+
26
+ turtle.done()
27
+ ```
28
+
29
+ همین! هر برنامه‌ای که با ماژول `turtle` استاندارد نوشته باشی، معمولاً فقط با
30
+ تغییر همین یک خط `import` روی ArcadeTurtle هم اجرا می‌شود.
31
+
32
+ پوشش API در سطح ماژول ۱۰۰٪ است (هر نام در `turtle.__all__` استاندارد این‌جا
33
+ هم وجود دارد). برای اثبات این ادعا، هفت «آزمون طلایی» در پوشه‌ی
34
+ [`examples/`](examples/) هستند: اسکریپت‌هایی به سبک رایج آموزش‌های اینترنتی
35
+ (بازی مار، پونگ، فضاپیمای مهاجم، Etch-A-Sketch، شکل ترکیبی، نقاشی با undo،
36
+ نمودار با مختصات جهانی) که فقط با تغییر `import` روی ArcadeTurtle اجرا
37
+ می‌شوند.
38
+
39
+ ## مثال‌ها
40
+
41
+ مثال‌ها در پوشه‌ی [`examples/`](examples/) از ساده به پیشرفته مرتب‌اند:
42
+
43
+ | فایل | چه چیزی یاد می‌دهد |
44
+ |---|---|
45
+ | [`00_healthcheck.py`](examples/00_healthcheck.py) | آیا Arcade و متن فارسی روی سیستم تو کار می‌کنند؟ |
46
+ | [`01_square.py`](examples/01_square.py) | اولین حرکت، چرخش و حلقه |
47
+ | [`02_star.py`](examples/02_star.py) | ستاره پنج‌پر، رنگ پس‌زمینه |
48
+ | [`03_polygon_function.py`](examples/03_polygon_function.py) | نوشتن تابع با پارامتر |
49
+ | [`04_color_spiral.py`](examples/04_color_spiral.py) | حلقه‌های تودرتو، `speed(0)` |
50
+ | [`05_filled_shapes.py`](examples/05_filled_shapes.py) | `begin_fill`/`end_fill`، `dot`، نوشتن فارسی |
51
+ | [`06_keyboard_drive.py`](examples/06_keyboard_drive.py) | کنترل با کیبورد (`onkeypress`) |
52
+ | [`07_click_paint.py`](examples/07_click_paint.py) | رویداد کلیک موس (`onscreenclick`) |
53
+ | [`08_snake_game.py`](examples/08_snake_game.py) | بازی کامل مار، چند لاک‌پشت، تابلوی امتیاز فارسی |
54
+ | [`golden_snake_original.py`](examples/golden_snake_original.py) | یک اسکریپت مار به سبک رایج اینترنتی — فقط با تغییر `import` اجرا می‌شود |
55
+ | [`09_compound_shape_art.py`](examples/09_compound_shape_art.py) | `Shape("compound")`، `addcomponent`، `stamp` |
56
+ | [`10_etch_a_sketch.py`](examples/10_etch_a_sketch.py) | `Turtle.ondrag` — کشیدن با موس |
57
+ | [`11_undo_drawing.py`](examples/11_undo_drawing.py) | نقاشی با کیبورد + `undo()` |
58
+ | [`12_world_coordinates_chart.py`](examples/12_world_coordinates_chart.py) | `setworldcoordinates` — رسم نمودار در واحدهای داده |
59
+ | [`13_pong.py`](examples/13_pong.py) | بازی دونفره، `ontimer`، برخورد |
60
+ | [`14_space_invaders.py`](examples/14_space_invaders.py) | `register_shape` با فایل تصویری، برخورد گلوله/دشمن |
61
+
62
+ ## جدول سازگاری با ماژول `turtle` استاندارد
63
+
64
+ ### ✅ کامل پشتیبانی می‌شود
65
+
66
+ حرکت: `forward`/`fd`، `backward`/`bk`/`back`، `left`/`lt`، `right`/`rt`،
67
+ `goto`/`setpos`/`setposition`، `setx`، `sety`، `home`، `setheading`/`seth` •
68
+ قلم: `penup`/`pu`/`up`، `pendown`/`pd`/`down`، `isdown`، `pensize`/`width`،
69
+ `pencolor`، `speed` • ترسیم: `circle`، `dot`، `stamp`، `clearstamp`،
70
+ `clearstamps`، `begin_fill`، `end_fill`، `filling`، `fillcolor`، `color`،
71
+ `write` (با پشتیبانی فارسی خودکار) • ظاهر: `shape`، `shapesize`/`turtlesize`،
72
+ `resizemode`، `tilt`، `tiltangle`، `settiltangle`، `shearfactor`،
73
+ `shapetransform`، `get_shapepoly`، `hideturtle`/`ht`، `showturtle`/`st`،
74
+ `isvisible` • شکل‌های سفارشی: `register_shape`/`addshape` (چندضلعی، شکل
75
+ ترکیبی compound، یا فایل تصویری PNG/GIF)، `Shape` class با `addcomponent`،
76
+ `getshapes` • کوئری: `position`/`pos` (به‌صورت `Vec2D`)، `xcor`، `ycor`،
77
+ `heading`، `towards`، `distance`، `teleport`، `degrees`، `radians` (واحد
78
+ اندازه‌گیری زاویه، جداگانه برای هر لاک‌پشت) • رویداد: `listen`، `onkey`،
79
+ `onkeypress`، `onkeyrelease`، `onclick`/`onscreenclick`، `ontimer`، `tracer`،
80
+ `update`، `Turtle.onclick`/`ondrag`/`onrelease` (کلیک/کشیدن روی خودِ
81
+ لاک‌پشت) • برگشت: `undo`، `setundobuffer`، `undobufferentries` (بافر
82
+ جداگانه برای هر لاک‌پشت) • ابزار: `pen` (گرفتن/ست‌کردن دسته‌ای وضعیت قلم)،
83
+ `getturtle`/`getpen`، `clone`، `begin_poly`/`end_poly`/`get_poly` (ضبط
84
+ چندضلعی دلخواه برای `register_shape` بعدی) • صفحه: `bgcolor`، `title`،
85
+ `colormode`، `mode` (`standard`/`logo`/`world`)، `setworldcoordinates`،
86
+ `bgpic`، `textinput`، `numinput`، `clear`، `reset`، `done`/`mainloop` •
87
+ کلاس‌ها: `Turtle()`/`Pen()`/`RawTurtle()`/`RawPen()` (چند نمونه مستقل)،
88
+ `Screen()` (سینگلتون) با `setup`، `window_width`/`window_height`،
89
+ `exitonclick`، `bye`، `Vec2D`.
90
+
91
+ `clear`/`reset` دقیقاً مثل turtle اصلی تفکیک شده‌اند: `Turtle.clear()` فقط
92
+ نقاشی‌های همان لاک‌پشت را پاک می‌کند، `Screen.clear()` همه‌چیز را، و
93
+ `Screen.reset()` علاوه بر آن همه لاک‌پشت‌ها را به حالت اولیه برمی‌گرداند.
94
+
95
+ ### 🟡 رفتار کمی متفاوت
96
+
97
+ - **`speed`**: عدد بین ۰ تا ۱۰ (یا نام‌های `fastest`/`fast`/`normal`/`slow`/
98
+ `slowest`)؛ سرعت واقعی پیکسل بر ثانیه با turtle اصلی یکی نیست، ولی معنای
99
+ نسبی (بزرگ‌تر = سریع‌تر، ۰ = آنی) همان است.
100
+ - **`colormode`**: مثل turtle اصلی کار می‌کند (پیش‌فرض ۲۵۵؛ برای رنگ‌های
101
+ اعشاری اول `colormode(1.0)` را صدا بزن) — ولی برخلاف turtle اصلی، حالت رنگ
102
+ سراسری است و به یک شیء Screen خاص گره نخورده.
103
+ - **شکل تصویری می‌چرخد** — برخلاف turtle اصلی که صراحتاً می‌گوید «شکل‌های
104
+ تصویری با چرخیدن لاک‌پشت نمی‌چرخند»، این‌جا عمداً برعکس تصمیم گرفته شده:
105
+ تصویر با heading می‌چرخد، چون هدف پشتیبانی از بازی‌های سبک Space Invaders/
106
+ مسابقه‌ای است که انتظار چرخش دارند. اگر تصویرت را با «رو به شرق» طراحی کنی
107
+ (نوکش رو به راست)، در heading=0 دقیقاً هم‌جهت با شکل‌های چندضلعی می‌ایستد.
108
+ - **`shearfactor`/`shapetransform`**: ساده‌سازی‌شده نسبت به فرمول داخلی
109
+ turtle اصلی؛ نتیجه‌ی بصری مشابه است ولی از نظر عددی («تجزیه»ی دقیق ماتریس)
110
+ یکی نیست. برای اکثر اسکریپت‌های آموزشی که این‌ها را جداگانه صدا می‌زنند
111
+ فرقی احساس نمی‌شود.
112
+ - **`resizemode("auto")`**: اندازه‌ی شکل برابر `pensize/5` محاسبه می‌شود —
113
+ یک تقریب مستند، نه فرمول دقیق stdlib.
114
+ - **`Turtle.onclick`/`ondrag`/`onrelease`**: تشخیص «روی لاک‌پشت» با یک دایره‌ی
115
+ احاطه‌کننده است، نه برخورد دقیق نقطه-در-چندضلعی — برای تعامل معمولی
116
+ (مثل کشیدن با موس) کافی است.
117
+ - **`undo`**: بافر جداگانه برای هر لاک‌پشت (نه یک بافر مشترک سراسری)،
118
+ و به‌جای بازسازی دقیق مثل stdlib، هر رکورد آنی state قبلی را برمی‌گرداند.
119
+ همان دستورهایی که stdlib هم undo می‌کند پوشش داده شده: حرکت، چرخش، مهر،
120
+ نوشته، `dot`، `begin_fill`/`end_fill`، و ویژگی‌های قلم. تغییرات ظاهری مثل
121
+ `shape`/`tilt`/`resizemode` در stdlib هم قابل undo نیستند.
122
+ - **`bgpic`**: تصویر پس‌زمینه با اندازه‌ی طبیعی خودش وسط‌چین می‌شود (مثل
123
+ stdlib) — کش داده نمی‌شود که کل پنجره را پر کند.
124
+ - **`textinput`/`numinput`**: با `tkinter.simpledialog` پیاده شده‌اند (چون
125
+ Arcade دیالوگ ورودی آماده ندارد)؛ یک پنجره‌ی جدا از پنجره‌ی اصلی باز
126
+ می‌شود. اگر tkinter روی سیستم در دسترس نباشد، خطای مهربان می‌دهند.
127
+
128
+ ### ❌ هنوز پشتیبانی نمی‌شود
129
+
130
+ `getscreen()` چندپنجره‌ای (فقط یک پنجره در هر زمان)، و `write(move=True)`.
131
+
132
+ این‌ها به‌صراحت خطای فارسی مهربان می‌دهند، نه رفتار خاموش یا نادرست.
133
+
134
+ دلیل `write(move=True)`: برای جابه‌جا کردن قلم به انتهای متن باید عرض متن
135
+ اندازه‌گیری شود که به پنجره‌ی باز نیاز دارد، ولی `write()` معمولاً قبل از
136
+ `done()` صدا زده می‌شود. به‌جایش خودت قلم را جابه‌جا کن:
137
+
138
+ ```python
139
+ penup()
140
+ write("سلام")
141
+ goto(xcor() + 60, ycor())
142
+ pendown()
143
+ ```
144
+
145
+ ## پشت صحنه چطور کار می‌کند؟
146
+
147
+ سه تصمیم معماری کلیدی (شرح کامل در [ROADMAP.md](ROADMAP.md)):
148
+
149
+ 1. **صف فرمان واحد**: هر فراخوانی دستور یک «فرمان» به یک صف FIFO مشترک
150
+ اضافه می‌کند. وضعیت منطقی لاک‌پشت (برای `position()` و مانند آن) بلافاصله
151
+ به‌روز می‌شود؛ پخش انیمیشنِ آن روی صفحه در فریم‌های بعدی انجام می‌شود.
152
+ 2. **تبدیل مختصات متمرکز**: تنها در `_coords.py` — مبدأ Turtle وسط صفحه،
153
+ مبدأ Arcade گوشه پایین-چپ.
154
+ 3. **بافر خط پخته‌شده**: خط‌های تمام‌شده یک‌بار در `ShapeElementList` Arcade
155
+ ساخته می‌شوند تا نقاشی‌های پیچیده (مثل مارپیچ) کند نشوند.
156
+
157
+ ## اجرای تست‌ها
158
+
159
+ ```bash
160
+ pip install -e ".[dev]"
161
+ pytest
162
+ ```