You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
@@ -156,10 +156,10 @@ of course you can stop the algorithm as soon as you have found the goal node
156
156
157
157
- the problem is symmetric - in the sense that you can start from the goal or from the starting position
158
158
- 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 !
160
160
161
161
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
163
163
```
164
164
165
165
+++
@@ -172,15 +172,16 @@ focusing on the Dijkstra algorithm and Python, here are a few hints that may hel
172
172
173
173
#### board representation (1)
174
174
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
176
176
177
-
- a sequence of 9 numbers
177
+
- a sequence (list or tuple) of 9 numbers
178
178
- a string of 9 characters
179
179
- a numpy array
180
180
- a class of your own
181
181
- ...
182
182
183
183
however, please pay attention to the fact that you will probably want to use boards:
184
+
184
185
- in structures like sets or dicts - so its needs to be **hashable**
185
186
- in a priority queue, which implies *sorting*, so you need a **sortable** representation as well
186
187
@@ -193,9 +194,10 @@ also please be aware that my early attempts at using a **numpy array** tended to
193
194
#### board representation (2)
194
195
195
196
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:
199
201
200
202
- in order to make it **hashable**, you just need to implement the following methods:
201
203
-`__eq__` to allow to compare boards
@@ -205,15 +207,16 @@ so, not that if you choose to use you own class:
205
207
`self.internal`, then the `__hash__` method will return `hash(self.internal)`
206
208
- and to make it **sortable**, you can go with implementing only `__lt__` (stands for *lower than*)
207
209
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>)
209
211
**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)
210
212
211
213
````{admonition} which is the cheapest ?
212
214
213
215
using a native Python data type - say, a `str` or a `tuple` - frees you from writing these 3 methods
214
216
but note that each of these 3 methods is .. a one-liner itself, so you actually spare exactly 6 lines of code
215
217
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...
217
220
````
218
221
219
222
+++
@@ -243,7 +246,7 @@ I'd like to outline in particular **the one below**, where you will find **very
243
246
````{admonition} this can **get you quickly up to speed**
other algorithms and languages are also discussed in other sections of the page
248
251
````
249
252
@@ -273,7 +276,7 @@ if you are done early, or want to pursue your work after the hackathon, you can
273
276
274
277
- alter your code so it works on any dimension, not just 3x3
275
278
- 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
277
280
this will show you why *A** is quite useful too, even if it's not guaranteed to be optimal
278
281
279
282
+++
@@ -283,37 +286,62 @@ if you are done early, or want to pursue your work after the hackathon, you can
283
286
as for the GUI, you can use any library you want; here we're going to quickly
284
287
describe [the `flet library`](https://flet.dev/) which is a simple Python
285
288
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
287
290
288
291
````{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
+
290
294
```bash
295
+
# the recommended way
291
296
flet run mycode.py
292
-
```
293
-
**and not simply**
294
-
```bash
297
+
298
+
# the not so good way
295
299
python mycode.py
296
300
```
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
299
303
````
300
304
301
305
+++
302
306
303
307
### flet widgets
304
308
305
309
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
307
311
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
311
323
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
314
340
```
315
341
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 :
317
345
318
346
```python
319
347
ft.Column([
@@ -338,9 +366,9 @@ and from then, your job is to
338
366
339
367
- keep references to the created widgets, in order to be able to interact with
340
368
them later
341
-
- find the right settings to make the widgets look like you want
342
369
- define the callbacks that will be called when the user interacts with the
343
370
widgets
371
+
- find the right settings to make the widgets look like you want
344
372
345
373
below are further hints and considerations, particularly for first-timers
346
374
@@ -387,12 +415,23 @@ one common mistake by first-timers is to
387
415
388
416
+++
389
417
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
+
390
427
### page.update()
391
428
392
429
also, as explained in the library documentation, don't forget to add `page.update()` whenever necessary
393
430
(you can also call update() on an inner widget if that's more convenient)
394
431
but without a call to this function, your changes will remain in memory and won't be reflected on the actual screen !
395
432
433
+
see v02 below for an example
434
+
396
435
```{admonition} do not overuse
397
436
:class: dropdown
398
437
@@ -402,49 +441,47 @@ so make sure to not call it too often either !
402
441
403
442
+++
404
443
405
-
### using globals or not
444
+
### starter code (optional)
406
445
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 helpin putting together your UI, here are the possible 2 first steps to get you started
411
448
412
-
+++
449
+
again this is just one of the many possible paths forward:
413
450
414
-
### using classes or not
451
+
#### ***in v01***
415
452
416
-
here again you can choose to use classes ornot; if you're not comfortable with
417
-
object-oriented programming, you can just use functions andglobal 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:
420
454
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`
422
459
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 inrange(9)]
429
-
returnself.squares
430
-
def update(board):
431
-
for i, square inzip(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 withv01
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.
448
485
449
486
+++
450
487
@@ -486,7 +523,7 @@ here is a little more information on how to use this stuff
486
523
487
524
+++
488
525
489
-
### the basics: sorting numbers
526
+
### with numbers
490
527
491
528
first with simple, atomic, objects:
492
529
@@ -508,7 +545,7 @@ while not Q.empty():
508
545
print(Q.get())
509
546
```
510
547
511
-
### sorting objects
548
+
### with objects
512
549
(label-dataclass-in-queue)=
513
550
514
551
in our case though, the items that we need to store in the queue are **not simple
0 commit comments