Skip to content

Commit 927e12e

Browse files
committed
puzzle8 comes with more details and figures and steps and a starter code
1 parent ad9da38 commit 927e12e

File tree

10 files changed

+377
-165
lines changed

10 files changed

+377
-165
lines changed

notebooks/tps/puzzle8/.teacher/README-puzzle8-corrige-nb.md

Lines changed: 102 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -156,10 +156,10 @@ of course you can stop the algorithm as soon as you have found the goal node
156156
157157
- the problem is symmetric - in the sense that you can start from the goal or from the starting position
158158
- and that **starting from the goal** has the extra advantage that you can **cache the results** of the algorithm
159-
and this way you case reuse them to solve another board !
159+
and this way you can reuse them to solve another board !
160160
161161
this might be a useful observation if you need to solve several problems in a row, and want to speed up the whole process
162-
please note that this is not at all crucial at first, and is useful only for optimization, so you can ignore it at first if you wish
162+
please note that this is not at all crucial at first, and is useful only for optimization, so you can ignore this at first if you wish
163163
```
164164

165165
+++
@@ -172,15 +172,16 @@ focusing on the Dijkstra algorithm and Python, here are a few hints that may hel
172172

173173
#### board representation (1)
174174

175-
you need to choose a way to represent a board; for that there are many options, like
175+
you need to choose a way to represent a board; for that you have many options, like
176176

177-
- a sequence of 9 numbers
177+
- a sequence (list or tuple) of 9 numbers
178178
- a string of 9 characters
179179
- a numpy array
180180
- a class of your own
181181
- ...
182182

183183
however, please pay attention to the fact that you will probably want to use boards:
184+
184185
- in structures like sets or dicts - so its needs to be **hashable**
185186
- in a priority queue, which implies *sorting*, so you need a **sortable** representation as well
186187

@@ -193,9 +194,10 @@ also please be aware that my early attempts at using a **numpy array** tended to
193194
#### board representation (2)
194195

195196
as part of this decision, you need to choose whether to model boards as **native Python** objects, or as **class instances**
196-
both approaches are valid, but the latter is likely to lead to a much cleaner code, and **more easily modifiable**, which is an important point when time
197-
matters
198-
so, not that if you choose to use you own class:
197+
both approaches are valid, but the latter is likely to lead to a much cleaner code, and **more easily modifiable**,
198+
which is an important point when time matters
199+
200+
so, note that if you choose to use you own class:
199201

200202
- in order to make it **hashable**, you just need to implement the following methods:
201203
- `__eq__` to allow to compare boards
@@ -205,15 +207,16 @@ so, not that if you choose to use you own class:
205207
`self.internal`, then the `__hash__` method will return `hash(self.internal)`
206208
- and to make it **sortable**, you can go with implementing only `__lt__` (stands for *lower than*)
207209
which is enough to describe order among instances of your class
208-
(for a deeper discussion, see also <https://docs.python.org/3/library/functools.html#functools.total_ordering>)
210+
(for a deeper discussion, see also <https://docs.python.org/3/library/functools.html#functools.total_ordering>)
209211
**note** however that in my solution, I did not even had to resort to that, thanks to `dataclass`, [more on this in this section below](label-dataclass-in-queue)
210212

211213
````{admonition} which is the cheapest ?
212214
213215
using a native Python data type - say, a `str` or a `tuple` - frees you from writing these 3 methods
214216
but note that each of these 3 methods is .. a one-liner itself, so you actually spare exactly 6 lines of code
215217
216-
on the other hand, using a native Python data type is likely to make your code more convoluted, so again, harder to tweak; so there's that...
218+
on the other hand, using a native Python data type is likely to make your code more convoluted, so again, harder to tweak;
219+
so there's that...
217220
````
218221

219222
+++
@@ -243,7 +246,7 @@ I'd like to outline in particular **the one below**, where you will find **very
243246
````{admonition} this can **get you quickly up to speed**
244247
:class: important
245248
particularly if you are unsure how to start !
246-
<https://www.redblobgames.com/pathfinding/a-star/implementation.html#python-search>
249+
<https://www.redblobgames.com/pathfinding/a-star/implementation.html#python-dijkstra>
247250
other algorithms and languages are also discussed in other sections of the page
248251
````
249252

@@ -273,7 +276,7 @@ if you are done early, or want to pursue your work after the hackathon, you can
273276

274277
- alter your code so it works on any dimension, not just 3x3
275278
- write a variant of the solver that uses the A\* algorithm,
276-
- and then compare both implementations on larger board sizes
279+
- and then compare both implementations on larger board sizes
277280
this will show you why *A** is quite useful too, even if it's not guaranteed to be optimal
278281

279282
+++
@@ -283,37 +286,62 @@ if you are done early, or want to pursue your work after the hackathon, you can
283286
as for the GUI, you can use any library you want; here we're going to quickly
284287
describe [the `flet library`](https://flet.dev/) which is a simple Python
285288
library that allows to create a GUI in a few lines of code; the resulting app
286-
can then be run a standalone app, or in a browser
289+
can then be run as a standalone app, or in a browser
287290

288291
````{admonition} warning: use flet run
289-
although your flet code sits in a `.py` file, you are supposed to run it with
292+
although your flet code sits in a `.py` file, you are supposed to run it like this
293+
290294
```bash
295+
# the recommended way
291296
flet run mycode.py
292-
```
293-
**and not simply**
294-
```bash
297+
298+
# the not so good way
295299
python mycode.py
296300
```
297-
the former is supposedly better in that it will reload changed sources dynamically (no need to restart the program after you change the source)
298-
the latter form should work too (but without dynamic loading, of course)
301+
the former is generally better, in that it will **reload changed sources dynamically** : no need to restart the program after you change the source! well, this does not work perfectly and a hard restart is sometimes needed, but it's still better than having to kill/restart your program ad nauseam
302+
the latter form should work too, but without dynamic loading, of course
299303
````
300304

301305
+++
302306

303307
### flet widgets
304308

305309
the basic idea is that the library gives you a `Page` object, that you can
306-
populate with widgets as Python objects, that your code can then interact with
310+
populate with a ***hierarchy of widgets*** as Python objects, that your code can then interact with
307311

308-
typically, in order to create a GUI that looks like this (this is just a
309-
suggestion of course, you can and should do whatever you want in terms of layout
310-
and capabilities):
312+
here are a few introductory words - feel free to skip of you have used the library already
313+
(but don't copy this code just yet, read also further on)
314+
315+
#### a simplistic program
316+
317+
```{image} media/puzzle8-00.png
318+
:width: 250px
319+
:align: right
320+
```
321+
322+
in its simplest form, a flet program looks like this
311323

312-
```{image} gui-sample.png
313-
:width: 300px
324+
- notice the way the application is started
325+
- also notice the way the layout is built by nesting a row inside the column
326+
- and how this layout is fined-tuned using alignment
327+
bookmark this for later, but the details are here <https://flet.dev/docs/reference/types/mainaxisalignment/>
328+
329+
````{admonition} a very basic, but working, flet program
330+
:class: dropdown
331+
```{literalinclude} puzzle8-00.py
332+
```
333+
````
334+
335+
#### in our case
336+
337+
```{image} media/gui-sample.png
338+
:width: 250px
339+
:align: right
314340
```
315341

316-
you would build a widget tree like this (but don't copy this code yet, read also further):
342+
typically, in order to create a GUI that looks like this (this is just a
343+
suggestion of course, you can and should do whatever you want in terms of layout
344+
and capabilities), you would instead build a widget tree like this :
317345

318346
```python
319347
ft.Column([
@@ -338,9 +366,9 @@ and from then, your job is to
338366

339367
- keep references to the created widgets, in order to be able to interact with
340368
them later
341-
- find the right settings to make the widgets look like you want
342369
- define the callbacks that will be called when the user interacts with the
343370
widgets
371+
- find the right settings to make the widgets look like you want
344372

345373
below are further hints and considerations, particularly for first-timers
346374

@@ -387,12 +415,23 @@ one common mistake by first-timers is to
387415

388416
+++
389417

418+
### using globals or not
419+
420+
now, with the code above, it's easy to "retrieve" the widgets, because they are now stored in the **global variable `squares`**
421+
this is considered a poor practice though, (mainly because global variables mean poor or no reusability)
422+
so once you get this working, you could optionnally try to be **a little smarter**, and avoid the global
423+
however that's an easy first step to get something working, if again you have never written anything like this before
424+
425+
+++
426+
390427
### page.update()
391428

392429
also, as explained in the library documentation, don't forget to add `page.update()` whenever necessary
393430
(you can also call update() on an inner widget if that's more convenient)
394431
but without a call to this function, your changes will remain in memory and won't be reflected on the actual screen !
395432

433+
see v02 below for an example
434+
396435
```{admonition} do not overuse
397436
:class: dropdown
398437

@@ -402,49 +441,47 @@ so make sure to not call it too often either !
402441

403442
+++
404443

405-
### using globals or not
444+
### starter code (optional)
406445

407-
now, with the code above, it's easy to "retrieve" the widgets, because they are now stored in the **global variable `squares`**
408-
this is considered a poor practice though, (mainly because global variables mean poor or no reusability)
409-
so once you get this working, you could optionnally try to be **a little smarter**, and avoid the global
410-
however that's an easy first step to get something working, if again you have never written anything like this before
446+
if you feel comfortable, just read on
447+
if you need more help in putting together your UI, here are the possible 2 first steps to get you started
411448

412-
+++
449+
again this is just one of the many possible paths forward:
413450

414-
### using classes or not
451+
#### ***in v01***
415452

416-
here again you can choose to use classes or not; if you're not comfortable with
417-
object-oriented programming, you can just use functions and global variables like above;
418-
however like most of the time, using classes will lead to a cleaner code, which
419-
is easier to maintain and to get right
453+
we create a totally passive UI; it is helpful so you can understand the basics:
420454

421-
consider e.g. this new structure:
455+
- it defines the game as **a function** that returns a `Column` that can be added to the page
456+
- it defines a `main(page)` function, which is a requirement of `flet`
457+
- we just call `ft.app(main)` that actually runs the whole UI
458+
it is `ft.app` that actually creates the `Page` (the main window) object, and passes it to `main`
422459

423-
```python
424-
class Board:
425-
def __init__(self, page):
426-
self.page = page
427-
def create_squares:
428-
self.squares = [ft.TextField(...) for i in range(9)]
429-
return self.squares
430-
def update(board):
431-
for i, square in zip(board, squares):
432-
square.value = i
433-
# with this approach it is MUCH EASIER to get our hands on the page instance !
434-
self.page.update()
435-
436-
def main(page):
437-
board = Board()
438-
page.add(
439-
ft.Column(
440-
... # the message area
441-
ft.GridView(board.create_squares(), ...), # main area
442-
ft.Row(
443-
... # the icons below
444-
),
445-
)
446-
)
447-
```
460+
don't copy this code either, v02 is more suitable as a starter code, but v01 should help you grasp how v02 actually works
461+
462+
`````{admonition} a possible v01
463+
:class: dropdown tip
464+
````{literalinclude} puzzle8-01.py
465+
````
466+
`````
467+
468+
#### ***in v02***
469+
470+
this version has a few changes as compared with v01
471+
472+
- this time it defines the game as **a class** that **inherits** the `Column` class
473+
- this way we can simply insert a `Puzzle8` instance in the page, just as it were a `Column` object (and to an extent, it is also a `Column` object thanks to the inheritance)
474+
- and the additional benefit is we can keep track of the various "pieces" in the game
475+
- this is illustrated in a sample callback `my_callback`, that we bind to the second button (`start_button`)
476+
477+
478+
`````{admonition} a possible v02
479+
:class: dropdown tip
480+
````{literalinclude} puzzle8-02.py
481+
````
482+
`````
483+
484+
you **could consider using this code as a starting point** for your app, provided that you have followed the construction up to this point of course.
448485

449486
+++
450487

@@ -486,7 +523,7 @@ here is a little more information on how to use this stuff
486523

487524
+++
488525

489-
### the basics: sorting numbers
526+
### with numbers
490527

491528
first with simple, atomic, objects:
492529

@@ -508,7 +545,7 @@ while not Q.empty():
508545
print(Q.get())
509546
```
510547

511-
### sorting objects
548+
### with objects
512549
(label-dataclass-in-queue)=
513550

514551
in our case though, the items that we need to store in the queue are **not simple
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
../puzzle8-01.py
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
../puzzle8-02.py

0 commit comments

Comments
 (0)