-
Notifications
You must be signed in to change notification settings - Fork 36
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Kristian Rother
committed
Jan 19, 2024
1 parent
aa73bf2
commit 82da3ee
Showing
4 changed files
with
208 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,105 @@ | ||
|
||
Debugging | ||
========= | ||
|
||
Debugging your code is a skill of its own. | ||
In this chapter, you find a list of debugging tools and techniques that you might want to try. | ||
|
||
Challenge: Maze Generator | ||
------------------------- | ||
|
||
There should be more levels in the dungeon levels. | ||
We could use a random algorithm to generate levels. | ||
They might look like this: | ||
|
||
:: | ||
|
||
########## | ||
#.#...#..# | ||
#........# | ||
#..##.##.# | ||
#.#..#...# | ||
#..#.....# | ||
##.##.##.# | ||
#........# | ||
#...#....# | ||
########## | ||
|
||
In :download:`generate_maze_buggy.py` you find a basic implementation of the algorithm. | ||
However, it is **very buggy**. There are about 20 bugs in the program. | ||
|
||
Types of Bugs | ||
------------- | ||
|
||
In Python, you can distinguish multiple types of bugs: | ||
|
||
1. SyntaxErrors | ||
+++++++++++++++ | ||
|
||
When there is a bug in the Python Syntax or indentation, Python will refuse to execute any code. | ||
From the error message you know that there is a problem and roughly where it is. | ||
|
||
Most of the time, syntax issues are fairly easy to fix. Your editor should highlight them right away. | ||
|
||
2. Runtime Exceptions | ||
+++++++++++++++++++++ | ||
|
||
When Python executes a part of the code but then crashes, you have an Exception at runtime. | ||
The ``NameError``, ``TypeError``, ``ValueError`` and many others fall in this category. | ||
The error message will give you some hints what to look for, but the source of the error might be somewhere else. | ||
In any case you know there is a problem, and Python knows it too. | ||
|
||
3. Semantic errors | ||
++++++++++++++++++ | ||
|
||
If Python executes the code without error, but does not deliver the expected result, | ||
you can call this a **Semantic Error**. | ||
You still know that there is a problem, but Python doesn't. | ||
Semantic errors are harder to debug. | ||
|
||
4. Complex issues | ||
+++++++++++++++++ | ||
|
||
More complex bugs are: race conditions (timing issues with multiple threads), | ||
border cases (bugs that only occur with exotic input), and Heisenbugs (bugs that disappear when you start debugging them). | ||
These are tough, and I won't cover them specifically here. | ||
|
||
5. Unknown Bugs | ||
+++++++++++++++ | ||
|
||
Finally, there might be bugs in the program that nobody knows about. | ||
This is of course bad, and we need to keep that possibility in the back of our heads. | ||
|
||
Debugging Techniques | ||
-------------------- | ||
|
||
* read the code | ||
* read the error message (message on bottom, line numbers, type of error on top) | ||
* inspect variables with `print(x)` | ||
* inspect the type of variables with `print(type(x))` | ||
* reproduce the bug | ||
* use minimal input data | ||
* use minimal number of iterations | ||
* isolate the bug by commenting parts of the program | ||
* drop assertions in your code | ||
* write more tests | ||
* explain the problem to someone else | ||
* step through the code in an interactive debugger | ||
* clean up your code | ||
* run a code cleanup tool (``black``) | ||
* run a type checker (``mypy``) | ||
* run a linter (``pylint``) | ||
* take a break | ||
* sleep over it | ||
* ask for a code review | ||
* write down what the problem is | ||
* draw a formal description of your program logic (flowchart, state diagram | ||
* draw a formal description of your data structure (class diagram, ER-diagram) | ||
* background reading on the library / algorithm you are implementing | ||
* google the error message | ||
|
||
|
||
.. seealso:: | ||
|
||
- `Debugging Tutorial <https://www.github.com/krother/debugging_tutorial>`__ | ||
- `Kristians Debugging Tutorial Video <https://www.youtube.com/watch?v=04paHt9xG9U>`__ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
# Maze Generator | ||
# | ||
# generates a random maze as a string | ||
# with '#' being walls and '.' being floors | ||
# | ||
# BUGGY CODE! | ||
# This code is full of bugs. | ||
# Try to fix them all. | ||
|
||
import random | ||
|
||
XMAX, YMAX = 12, 7 | ||
|
||
|
||
def create_grid_string(floors: set[tuple[int, int]], xsize: int, ysize: int) -> str: | ||
""" | ||
Creates a grid of size (xsize, ysize) | ||
from the given positions of floors. | ||
""" | ||
for y in range(ysize): | ||
grid = "" | ||
for x in range(xsize): | ||
grid = "#" if (xsize, ysize) in floors else "." | ||
grid == "\n" | ||
return grid | ||
|
||
|
||
def get_all_floor_positions(xsize: int, ysize: int) | ||
"""Returns a list of (x, y) tuples covering all positions in a grid""" | ||
return [(x, y) for x in range(0, xsize) for y in range(1, ysize - 1)] | ||
|
||
|
||
def get_neighbors(x: int, y: int) -> list[tuple(int, int)]: | ||
"""Returns a list with the 8 neighbor positions of (x, y)""" | ||
return [ | ||
(x, - 1), (y, x + 1), (x - (1), y), (x + 1), y, | ||
(x, (-1, y)), (x + 1, y, 1), (x - 1, y + 1, x + 1, y + 1) | ||
] | ||
|
||
|
||
def generate_floor_positions(xsize: int, ysize:int) -> set[tuple[int, int]]: | ||
""" | ||
Creates positions of floors for a random maze | ||
1. pick a random location in the maze | ||
2. count how many of its neighbors are already floors | ||
3. if there are 4 or less, make the position a floor | ||
4. continue with step 1 until every location has been visited once | ||
""" | ||
positions = get_all_floor_positions(xsize, ysize) | ||
floors = set() | ||
while positions != []: | ||
x, y = random.choice(positions) | ||
neighbors = get_neighbors(x, y) | ||
free = [for nb in neighbors if nb in floors] | ||
if len(free) > 5: | ||
floors.add((x, y)) | ||
positions.remove((x, y)) | ||
return floors | ||
|
||
|
||
def create_maze(xsize: int, ysize: int): | ||
"""Returns a xsize*ysize maze as a string""" | ||
floors = generate_floor_position(xsize, ysize) | ||
maze = create_grid_string(floors, xsize, ysize) | ||
|
||
|
||
if __name__ == '__main__': | ||
maze = create_maze | ||
print(maze) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
|
||
Interactive Python Debugger | ||
=========================== | ||
|
||
The ``ipdb`` program is an *interactive debugger* that allows you to execute a program in slow motion. | ||
|
||
Install it with: | ||
|
||
:: | ||
|
||
pip install ipdb | ||
|
||
Then you can start your program in debugging mode from the terminal: | ||
|
||
:: | ||
|
||
python -m ipdb dungeon_explorer.py | ||
|
||
In ``ipdb`` you have a couple of keyboard shortcuts that allow you to navigate the programs execution: | ||
|
||
=================== ======================================================================== | ||
command description | ||
=================== ======================================================================== | ||
``n`` execute the next line | ||
``s`` execute the next line. If it contains a function, step inside it | ||
``b 57`` set a breakpoint at line 57 | ||
``c`` continue execution until the next breakpoint | ||
``ll`` list lots of lines around the current one | ||
``myvar`` print contents of the variable ``myvar`` | ||
``myvar = 1`` modify a variable | ||
=================== ======================================================================== |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters