diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c75eecc --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/public diff --git a/config.toml b/config.toml new file mode 100644 index 0000000..208278c --- /dev/null +++ b/config.toml @@ -0,0 +1,16 @@ +# The URL the site will be built for +base_url = "https://fliegendewurst.eu" + +# Whether to automatically compile all Sass files in the sass directory +compile_sass = false + +# Whether to build a search index to be used later on by a JavaScript library +build_search_index = false + +[markdown] +# Whether to do syntax highlighting +# Theme can be customised by setting the `highlight_theme` variable to a theme supported by Zola +highlight_code = true + +[extra] +# Put all your custom variables here diff --git a/content/blog/_index.md b/content/blog/_index.md new file mode 100644 index 0000000..72bc989 --- /dev/null +++ b/content/blog/_index.md @@ -0,0 +1,6 @@ ++++ +title = "List of blog posts" +sort_by = "date" +template = "blog.html" +page_template = "blog-page.html" ++++ diff --git a/raspberry-pi-temperature-monitoring.html b/content/blog/raspberry-pi-temperature-monitoring.md similarity index 80% rename from raspberry-pi-temperature-monitoring.html rename to content/blog/raspberry-pi-temperature-monitoring.md index ea2c5de..6f2a1a0 100644 --- a/raspberry-pi-temperature-monitoring.html +++ b/content/blog/raspberry-pi-temperature-monitoring.md @@ -1,31 +1,20 @@ - - -Raspberry Pi temperature monitoring + calendar - - - - - ++++ +title = "Raspberry Pi 0 W + sensors + OLED display = temperature monitoring and calendar" +date = 2022-10-05 +updated = 2023-10-02 ++++ - -
-

FliegendeWurst's corner of the WWW

+OLED display showing temperature and upcoming appointments, connected to a Raspberry Pi -

Raspberry Pi 0 W + sensors + OLED display = temperature monitoring and calendar

-
- -OLED display showing temperature and upcoming appointments, connected to a Raspberry Pi - -

With this project, I monitor the temperature and humidity in my room. Measurements are taken every 10 minutes and saved in an SQLite database. Additionally, the display shows the upcoming appointments of the next five days: each of the 24 rows represents one hour and each pixel in a row represents 5 minutes. In the middle, the time until the next appointment, the relative humidity and the current temperature are shown. Below that, a graph shows the temperature of the last two days. When activating another display mode (not active in the picture), this area of the display instead shows the next seven appointments. -

-

Hardware

+ +### Hardware
-
- diff --git a/tree-style-tabs-new-tab-button-bottom.html b/content/blog/tree-style-tabs-new-tab-button-bottom.md similarity index 58% rename from tree-style-tabs-new-tab-button-bottom.html rename to content/blog/tree-style-tabs-new-tab-button-bottom.md index 6a641d2..9b53be7 100644 --- a/tree-style-tabs-new-tab-button-bottom.html +++ b/content/blog/tree-style-tabs-new-tab-button-bottom.md @@ -1,21 +1,7 @@ - - - -Fixing the position of TST's new tab button - - - - - - - -
-

FliegendeWurst's corner of the WWW

- -

Fixing the position of TST's new tab button

- -
- ++++ +title = "Fixing the position of TST's new tab button" +date = 2023-07-30 ++++

Here you can learn how to change the position of the new tab button for the Tree Style Tab browser extension. With this change, the new tab button is always at the bottom of the tab tree. @@ -26,7 +12,8 @@ No more aiming at a 20 pixels high region below the last opened tab!

Simply add the CSS code below to TST's custom style option ("Advanced" > "Extra style rules for contents provided by Tree Style Tab").

-
+
+```css
 #tabbar .after-tabs {
   display: none;
 }
@@ -38,9 +25,4 @@ Simply add the CSS code below to TST's custom style option ("Advanced" > "Extra
 :root {
   --after-tabs-area-size: 20px !important;
 }
-
- -
-
- - \ No newline at end of file +``` \ No newline at end of file diff --git a/z3-logic-puzzle-solving.html b/content/blog/z3-logic-puzzle-solving.md similarity index 90% rename from z3-logic-puzzle-solving.html rename to content/blog/z3-logic-puzzle-solving.md index 19d405f..6e2869b 100644 --- a/z3-logic-puzzle-solving.html +++ b/content/blog/z3-logic-puzzle-solving.md @@ -1,23 +1,11 @@ - - -Solving a logic puzzle using an SMT solver - - - - - - - -
-

FliegendeWurst's corner of the WWW

- -

Solving a logic puzzle using an SMT solver

- -
++++ +title = "Solving the Logelei off-by-one puzzle using Z3" +date = 2022-11-30 ++++ In this blog post, I'll describe how to solve a logic puzzle using Z3, an automated SMT solver. -

The puzzle

+### The puzzle

You are given a rectangular grid where each cell may either contain a number (between 1 and 5) or be empty. @@ -29,7 +17,7 @@ But there's a twist: due to a transmission error, all of the sums are off by one The (already solved) 4-by-4 grid below illustrates these constraints.
-example puzzle +example puzzle

@@ -49,33 +37,36 @@ The precise installation steps vary by operating system, check the official docu In the following, I will describe the code required to explain the puzzle to Z3. First, we need to import the z3 Python module. To avoid prefixing every constructor with z3., we import all symbols into the local namespace. We also import the math module (we will need it later). We also create a new Solver object for later use. -
+
+```python
 from z3 import *
 import math
 
 s = Solver()
-
+``` Problems are specified by creating a bunch of variables (booleans, integers, ...) and constraints on these variables. Z3 will then produce a model that assigns a value to each variable if the constraints are satisfiable. Since we are interested in the values of the grid cells, we create an integer variable for each cell. FreshInt returns a new integer variable that isn't identical to any other previously created variable. -
+
+```python
 # n*n grid
 n = 6
 
 grid = [[FreshInt() for x in range(n)] for y in range(n)]
-
+```

Specifying the constraints

Empty grid cells will be indicated by a value of zero. All other cells need to be filled with a number between one and five. To implement this constraint, we iterate over each row and cell of the grid and add the constraint (integer value must be at least 0 and at most 5) to the solver. -
+
+```python
 for row in grid:
     for cell in row:
-        s.add(cell >= 0)
-        s.add(cell <= 5)
-
+ s.add(cell >= 0) + s.add(cell <= 5) +``` For each row/column, the numbers used must be unique. This is modeled by pairwise inequality of the variables making up that row or column. @@ -83,34 +74,38 @@ Of course, a value of zero (= empty grid cell) may appear more than once. We define a function distinct_if_nonzero that, given a list of variables, adds constraints to ensure that any two variables are not equal if both of them are nonzero. This is done by supplying three parameters to the If function: the condition, the constraint if the condition is true, the constraint if the condition is false. -
+
+```python
 def distinct_if_nonzero(x):
     for i in range(len(x)):
         for j in range(i+1, len(x)):
             cell_i = x[i]
             cell_j = x[j]
             s.add(If(And(cell_i != 0, cell_j != 0), cell_i != cell_j, True))
-
+``` Then we simply apply this function to each row and column of the grid. -
+
+```python
 for row in grid:
     distinct_if_nonzero(row)
 
 for column in [[grid[y][x] for y in range(n)] for x in range(n)]:
     distinct_if_nonzero(column)
-
+``` The sums indicated next to the grid are represented by integer variables. It is quite obvious that the maximum number of segments possible in each row is achieved if every second cell is filled with a number. As such, we only need to keep track of that many segment sums. -
+
+```python
 num_sums = math.ceil(n / 2)
 sums_h = [[FreshInt() for i in range(num_sums)] for y in range(n)]
 sums_v = [[FreshInt() for i in range(num_sums)] for x in range(n)]
-
+``` Connecting these sums to the cell variables is done by considering each binary pattern p of numbered / not numbered cells. -
+
+```python
 # iterate over each row of the grid
 for y in range(n):
     # iterate over each binary pattern
@@ -123,7 +118,7 @@ for y in range(n):
         # iterate over each grid cell
         for x in range(n):
             # check that binary digit in the pattern
-            if (p >> x) & 1 == 1:
+            if (p >> x) & 1 == 1:
                 mc = And(mc, grid[y][x] != 0)
                 if start_next_segment:
                     segments.append([grid[y][x]])
@@ -135,7 +130,7 @@ for y in range(n):
                 start_next_segment = True
         # specify sums value of each segment
         for k in range(num_sums):
-            if k < len(segments):
+            if k < len(segments):
                 off_by_one = Or(
                     sums_h[y][k] == sum(segments[k]) + 1,
                     sums_h[y][k] == sum(segments[k]) - 1
@@ -143,14 +138,15 @@ for y in range(n):
                 s.add(If(mc, off_by_one, True))
             else:
                 s.add(If(mc, sums_h[y][k] == -1, True))
-
+``` The same is done for the columns of the grid. For brevity, that code is omitted from this post. The full source code is linked at the end of the article.

Finding and displaying a solution

We still need to encode the off-by-one sums given in the puzzle as constraints. This is fairly simple: -
+
+```python
 def add_spec(spec_h, spec_v):
     for a, b in zip(spec_h, sums_h):
         for x, y in zip(a, b):
@@ -177,7 +173,7 @@ spec_v = [
 ]
 s.push()
 add_spec(spec_h, spec_v)
-
+``` Note that we used s.push() to create a nested constraint context. Any constraints added after this call may be removed by calling s.pop(). We will make use of this later to generate new puzzles (with new off-by-one sums). @@ -193,7 +189,8 @@ Z3 only returns unknown if it is not able to dedu The loop below uses a simple trick to enumerate all solutions to the puzzle: after one solution is found, add a constraint that at least one grid cell must be different.

-
+
+```python
 res = s.check()
 print(res)
 
@@ -209,7 +206,7 @@ while res == sat:
     res = s.check()
 print("no other solutions")
 s.pop()
-
+```

Generating puzzles with a unique solution

@@ -223,7 +220,8 @@ If no other solution is found, we know that the puzzle has a unique solution. Otherwise, we add a constraint that bans this particular off-by-one sums configuration (this ensures we always try new puzzles).

-
+
+```python
 # try to find another puzzle with a unique solution
 while True:
     s.push()
@@ -253,12 +251,6 @@ while True:
         input("continue? [press enter]")
     s.pop()
     s.add(c2)
-
+``` -Full source code - - - -
-
- \ No newline at end of file +Full source code \ No newline at end of file diff --git a/main.css b/main.css deleted file mode 100644 index 6fe4f07..0000000 --- a/main.css +++ /dev/null @@ -1,62 +0,0 @@ -#favicon { - width: 2.5em; - height: 2.5em; - margin-right: 0.25em; -} - -#header { - display: flex; - flex-direction: row; - align-items: center; -} - -article { - max-width: 45em; -} - -main { - max-width: 60em; - margin-left: auto; - margin-right: auto; - background-color: #d6cebf; - border: 0.3em outset gold; - border-radius: 1em; - padding: 1em; -} - -#hero-img { - width: 30em; - padding: 3em; -} - -.fancy-name, .code { - font-family: monospace; -} - -.entry-footer { - margin-top: 1em; - border-top: 1px solid #005e75; - padding-top: 0.5em; -} - -@media print { - body { - background-color: white; - } -} - -@media (prefers-color-scheme: dark) { - html { - color: #fff; - background-color: #000; - } - a { - color: #aaf; - } - a:visited { - color: #faf; - } - main { - background-color: #222; - } -} \ No newline at end of file diff --git a/static/ApollonianCirclePackings/index.html b/static/ApollonianCirclePackings/index.html new file mode 100644 index 0000000..4de6797 --- /dev/null +++ b/static/ApollonianCirclePackings/index.html @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/circuit.svg b/static/assets/circuit.svg similarity index 100% rename from circuit.svg rename to static/assets/circuit.svg diff --git a/logelei_off_by_one_sums.py b/static/assets/logelei_off_by_one_sums.py similarity index 100% rename from logelei_off_by_one_sums.py rename to static/assets/logelei_off_by_one_sums.py diff --git a/raspberry-pi-temperature-monitoring.jpg b/static/assets/raspberry-pi-temperature-monitoring.jpg similarity index 100% rename from raspberry-pi-temperature-monitoring.jpg rename to static/assets/raspberry-pi-temperature-monitoring.jpg diff --git a/sample-puzzle.png b/static/assets/sample-puzzle.png similarity index 100% rename from sample-puzzle.png rename to static/assets/sample-puzzle.png diff --git a/favicon.ico b/static/favicon.ico similarity index 100% rename from favicon.ico rename to static/favicon.ico diff --git a/static/kv/index.html b/static/kv/index.html new file mode 100644 index 0000000..cbb7345 --- /dev/null +++ b/static/kv/index.html @@ -0,0 +1,64 @@ + + + + KV diagram calculator + + + + + + + + +

KV diagram calculator

(source code)
+
+
+ +
+

+ Change output specification by scrolling (up => 1, down => 0), typing (=> 1, 0) or deleting (=> -). +

+

+ Load example 1, example 2, example 3. + Set all cells to , or . +

+
+
+
+ +
+
+
+ + + \ No newline at end of file diff --git a/static/qwörtle/index.html b/static/qwörtle/index.html new file mode 100644 index 0000000..ac6b3c4 --- /dev/null +++ b/static/qwörtle/index.html @@ -0,0 +1,31 @@ + + + +Qwörtle + + + +
+

Qwörtle

+
+

+	
+	

In die Zwischenablage kopiert!

+
+
+
+
+
+
+ +
+ + +
+ +
+ + diff --git a/static/sensor-dashboard/index.html b/static/sensor-dashboard/index.html new file mode 100644 index 0000000..65c9e2d --- /dev/null +++ b/static/sensor-dashboard/index.html @@ -0,0 +1,40 @@ + + + + + Sensor dashboard + + + + + + + +
+ + + + + + diff --git a/templates/base.html b/templates/base.html new file mode 100644 index 0000000..a38677e --- /dev/null +++ b/templates/base.html @@ -0,0 +1,28 @@ + +{% block html_tag %} {% endblock %} + +FliegendeWurst's corner of the WWW + + + + + + + +
+

FliegendeWurst's corner of the WWW

+{% block content %} {% endblock %} +
+
+ + © FliegendeWurst, 2022-2023. + Source: github.com/FliegendeWurst/fliegendewurst.eu. +
+ This work is licensed under a + + Creative Commons Attribution 4.0 International License. +
+
+
+ + \ No newline at end of file diff --git a/templates/blog-page.html b/templates/blog-page.html new file mode 100644 index 0000000..d3dcdb9 --- /dev/null +++ b/templates/blog-page.html @@ -0,0 +1,15 @@ +{% extends "base.html" %} +{% block html_tag %} + +{% endblock html_tag %} +{% block content %} +
+

+ {{ page.title }} +

+

{{ page.date }}{% if page.updated %} +(updated ) +{% endif %}

+{{ page.content | safe }} +
+{% endblock content %} \ No newline at end of file diff --git a/templates/blog.html b/templates/blog.html new file mode 100644 index 0000000..91b3f9a --- /dev/null +++ b/templates/blog.html @@ -0,0 +1,17 @@ +{% extends "base.html" %} + +{% block html_tag %} + +{% endblock html_tag %} + +{% block content %} +

+ {{ section.title }} +

+ +{% endblock content %} \ No newline at end of file diff --git a/index.html b/templates/index.html similarity index 81% rename from index.html rename to templates/index.html index 93fa693..21ed1c1 100644 --- a/index.html +++ b/templates/index.html @@ -1,27 +1,24 @@ - -FliegendeWurst's corner of the WWW - - - - +{% extends "base.html" %} - -
-

FliegendeWurst's corner of the WWW

+{% block html_tag %} + +{% endblock html_tag %} -Interesting tidbits +{% block content %} +Posts -Check out my projects: +Projects
- \ No newline at end of file +{% endblock content %} \ No newline at end of file