Skip to content

Commit b399f48

Browse files
committed
Refactor tutorial
More verbose code examples Include future annotations Explain procedural block elements function call
1 parent 027f522 commit b399f48

File tree

2 files changed

+72
-19
lines changed

2 files changed

+72
-19
lines changed

docs/tutorial/part-00.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,13 @@ First script
2323

2424
First start with a modern top-level script.
2525
Create a script in the project root folder called ``main.py`` which checks :python:`if __name__ == "__main__":` and calls a ``main`` function.
26+
Any modern script using type-hinting will also have :python:`from __future__ import annotations` near the top.
2627

2728
.. code-block:: python
2829
30+
from __future__ import annotations
31+
32+
2933
def main() -> None:
3034
print("Hello World!")
3135

docs/tutorial/part-01.rst

Lines changed: 68 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@ You should have ``main.py`` script from :ref:`part-0`:
1717

1818
.. code-block:: python
1919
20+
from __future__ import annotations
21+
22+
2023
def main() -> None:
2124
...
2225
@@ -36,6 +39,7 @@ These kinds of tilesets are always loaded with :python:`columns=16, rows=16, cha
3639
Use the string :python:`"data/Alloy_curses_12x12.png"` to refer to the path of the tileset. [#why_not_pathlib]_
3740

3841
Load the tileset with :any:`tcod.tileset.load_tilesheet`.
42+
Pass the tileset to :any:`tcod.tileset.procedural_block_elements` which will fill in most `Block Elements <https://en.wikipedia.org/wiki/Block_Elements>`_ missing from `Code Page 437 <https://en.wikipedia.org/wiki/Code_page_437>`_.
3943
Then pass the tileset to :any:`tcod.context.new`, you only need to provide the ``tileset`` parameter.
4044

4145
:any:`tcod.context.new` returns a :any:`Context` which will be used with Python's :python:`with` statement.
@@ -45,9 +49,10 @@ The new block can't be empty, so add :python:`pass` to the with statement body.
4549
These functions are part of modules which have not been imported yet, so new imports for ``tcod.context`` and ``tcod.tileset`` must be added to the top of the script.
4650

4751
.. code-block:: python
48-
:emphasize-lines: 2,3,8-12
52+
:emphasize-lines: 3,4,8-14
53+
54+
from __future__ import annotations
4955
50-
...
5156
import tcod.context # Add these imports
5257
import tcod.tileset
5358
@@ -57,9 +62,13 @@ These functions are part of modules which have not been imported yet, so new imp
5762
tileset = tcod.tileset.load_tilesheet(
5863
"data/Alloy_curses_12x12.png", columns=16, rows=16, charmap=tcod.tileset.CHARMAP_CP437
5964
)
65+
tcod.tileset.procedural_block_elements(tileset=tileset)
6066
with tcod.context.new(tileset=tileset) as context:
6167
pass # The window will stay open for the duration of this block
62-
...
68+
69+
70+
if __name__ == "__main__":
71+
main()
6372
6473
If an import fails that means you do not have ``tcod`` installed on the Python environment you just used to run the script.
6574
If you use an IDE then make sure the Python environment it is using is correct and then run :shell:`pip install tcod` from the shell terminal within that IDE.
@@ -88,18 +97,22 @@ Then test if an event is for closing the window with :python:`if isinstance(even
8897
If this is True then you should exit the function with :python:`raise SystemExit()`. [#why_raise]_
8998

9099
.. code-block:: python
91-
:emphasize-lines: 2,3,11-19
100+
:emphasize-lines: 3,5,15-23
101+
102+
from __future__ import annotations
92103
93-
...
94104
import tcod.console
105+
import tcod.context
95106
import tcod.event
107+
import tcod.tileset
96108
97109
98110
def main() -> None:
99111
"""Show "Hello World" until the window is closed."""
100112
tileset = tcod.tileset.load_tilesheet(
101113
"data/Alloy_curses_12x12.png", columns=16, rows=16, charmap=tcod.tileset.CHARMAP_CP437
102114
)
115+
tcod.tileset.procedural_block_elements(tileset=tileset)
103116
console = tcod.console.Console(80, 50)
104117
console.print(0, 0, "Hello World") # Test text by printing "Hello World" to the console
105118
with tcod.context.new(console=console, tileset=tileset) as context:
@@ -109,7 +122,10 @@ If this is True then you should exit the function with :python:`raise SystemExit
109122
print(event)
110123
if isinstance(event, tcod.event.Quit):
111124
raise SystemExit()
112-
...
125+
126+
127+
if __name__ == "__main__":
128+
main()
113129
114130
If you run this then you get a window saying :python:`"Hello World"`.
115131
The window can be resized and the console will be stretched to fit the new resolution.
@@ -135,9 +151,15 @@ The parameters for ``on_draw`` are ``self`` because this is an instance method a
135151
Call this method using the players current coordinates and the :python:`"@"` character.
136152

137153
.. code-block:: python
154+
:emphasize-lines: 3,10-21
155+
156+
from __future__ import annotations
138157
139-
...
140158
import attrs
159+
import tcod.console
160+
import tcod.context
161+
import tcod.event
162+
import tcod.tileset
141163
142164
143165
@attrs.define(eq=False)
@@ -152,6 +174,7 @@ Call this method using the players current coordinates and the :python:`"@"` cha
152174
def on_draw(self, console: tcod.console.Console) -> None:
153175
"""Draw the player glyph."""
154176
console.print(self.player_x, self.player_y, "@")
177+
155178
...
156179
157180
Now remove the :python:`console.print(0, 0, "Hello World")` line from ``main``.
@@ -183,7 +206,10 @@ Modify the drawing routine so that the console is cleared, then passed to :pytho
183206
print(event)
184207
if isinstance(event, tcod.event.Quit):
185208
raise SystemExit()
186-
...
209+
210+
211+
if __name__ == "__main__":
212+
main()
187213
188214
Now if you run the script you'll see ``@``.
189215

@@ -201,12 +227,33 @@ Make a case for each arrow key: ``LEFT`` ``RIGHT`` ``UP`` ``DOWN`` and move the
201227
Since events are printed you can check the :any:`KeySym` of a key by pressing that key and looking at the printed output.
202228
See :any:`KeySym` for a list of all keys.
203229

230+
Finally replace the event handling code in ``main`` to defer to the states ``on_event`` method.
231+
The full script so far is:
232+
204233
.. code-block:: python
234+
:emphasize-lines: 23-35,53
235+
236+
from __future__ import annotations
237+
238+
import attrs
239+
import tcod.console
240+
import tcod.context
241+
import tcod.event
242+
import tcod.tileset
243+
205244
206-
...
207245
@attrs.define(eq=False)
208246
class ExampleState:
209-
...
247+
"""Example state with a hard-coded player position."""
248+
249+
player_x: int
250+
"""Player X position, left-most position is zero."""
251+
player_y: int
252+
"""Player Y position, top-most position is zero."""
253+
254+
def on_draw(self, console: tcod.console.Console) -> None:
255+
"""Draw the player glyph."""
256+
console.print(self.player_x, self.player_y, "@")
210257
211258
def on_event(self, event: tcod.event.Event) -> None:
212259
"""Move the player on events and handle exiting. Movement is hard-coded."""
@@ -221,16 +268,15 @@ See :any:`KeySym` for a list of all keys.
221268
self.player_y -= 1
222269
case tcod.event.KeyDown(sym=tcod.event.KeySym.DOWN):
223270
self.player_y += 1
224-
...
225-
226-
Now replace the event handling code in ``main`` to defer to the states ``on_event`` method.
227271
228-
.. code-block:: python
229-
:emphasize-lines: 12
230272
231-
...
232273
def main() -> None:
233-
...
274+
"""Run ExampleState."""
275+
tileset = tcod.tileset.load_tilesheet(
276+
"data/Alloy_curses_12x12.png", columns=16, rows=16, charmap=tcod.tileset.CHARMAP_CP437
277+
)
278+
tcod.tileset.procedural_block_elements(tileset=tileset)
279+
console = tcod.console.Console(80, 50)
234280
state = ExampleState(player_x=console.width // 2, player_y=console.height // 2)
235281
with tcod.context.new(console=console, tileset=tileset) as context:
236282
while True:
@@ -240,7 +286,10 @@ Now replace the event handling code in ``main`` to defer to the states ``on_even
240286
for event in tcod.event.wait():
241287
print(event)
242288
state.on_event(event) # Pass events to the state
243-
...
289+
290+
291+
if __name__ == "__main__":
292+
main()
244293
245294
Now when you run this script you have a player character you can move around with the arrow keys before closing the window.
246295

@@ -253,7 +302,7 @@ You can review the part-1 source code `here <https://github.com/HexDecimal/pytho
253302
254303
.. [#why_not_pathlib]
255304
:any:`pathlib` is not used because this example is too simple for that.
256-
The working directory will be the project root folder for the entire tutorial, including distributions.
305+
The working directory will always be the project root folder for the entire tutorial, including distributions.
257306
:any:`pathlib` will be used later for saved games and configuration directories, and not for static data.
258307
259308
.. [#init_context] This tutorial follows the setup for a fixed-size console.

0 commit comments

Comments
 (0)