Traduire des éléments entre langues révèle comment chaque langue aborde différents compromis de conception, et je pense que c’est un exercice utile. Avoir quelque chose à traduire est la première étape. J’ai trouvé une intrigue que je voulais générer, et du code qui la reproduisait, alors c’est parti !
Je ne me souviens pas comment j’ai trouvé cette page à l’origine (je n’ai pas gardé de note dessus, semble-t-il) et elle est restée trop longtemps sur ma pile de sujets à publier, alors voici le message que j’avais l’intention d’écrire.
Cet article détaille le code ALGOL qui génère l’art généré par ordinateur “Schotter” de Georg Nees de 1968, qui montre une grille de carrés qui sont de plus en plus déplacés en position et en rotation.
1 'BEGIN''COMMENT'SCHOTTER., 2 'REAL'R,PIHALB,PI4T., 3 'INTEGER'I., 4 'PROCEDURE'QUAD., 5 'BEGIN' 6 'REAL'P1,Q1,PSI.,'INTEGER'S., 7 JE1.=5*1/264.,JA1.=-JE1., 8 JE2.=PI4T*(1+I/264).,JA2.=PI4T*(1-I/264)., 9 P1.=P+5+J1.,Q1.=Q+5+J1.,PS1.=J2., 10 LEER(P1+R*COS(PSI),Q1+R*SIN(PSI))., 11 'FOR'S.=1'STEP'1'UNTIL'4'DO' 12 'BEGIN'PSI.=PSI+PIHALB., 13 LINE(P1+R*COS(PSI),Q1+R*SIN(PSI))., 14 'END".,I.=I+1 15 'END'QUAD., 16 R.=5*1.4142., 17 PIHALB.=3.14159*.5.,P14T.=PIHALB*.5., 18 I.=0., 19 SERIE(10.0,10.0,22,12,QUAD) 20 'END' SCHOTTER., 1 'REAL'P,Q,P1,Q1,XM,YM,HOR,VER,JLI,JRE,JUN,JOB., 5 'INTEGER'I,M,M,T., 7 'PROCEDURE'SERIE(QUER,HOCH,XMAL,YMAL,FIGUR)., 8 'VALUE'QUER,HOCH,XMAL,YMAL., 9 'REAL'QUER,HOCH., 10 'INTEGER'XMAL,YMAL., 11 'PROCEDURE'FIGUR., 12 'BEGIN' 13 'REAL'YANF., 14 'INTEGER'COUNTX,COUNTY., 15 P.=-QUER*XMAL*.5., 16 Q.=YANF.=-HOCH*YMAL*.5., 17 'FOR'COUNTX.=1'STEP'1'UNTIL'XMAL'DO' 18 'BEGIN'Q.=YANF., 19 'FOR'COUNTY.=1'STEP'1'UNTIL'YMAL'DO' 20 'BEGIN'FIGUR.,Q.=Q+HOCH 21 'END'.,P.=P+QUER 22 'END'., 23 LEER(-148.0,-105.0).,CLOSE., 24 SONK(11)., 25 OPBEN(X,Y) 26 'END'SERIE.,
gravier
Ce qui manque dans ce code ALGOL, ce sont les graines nécessaires à la reproduction de la parcelle. L’auteur s’est rendu dans un terrier de lapin pour enquêter et calculer différentes valeurs, mais a réussi à les déterminer comme étant « (1922110153) pour la graine de décalage x et y, et (1769133315) pour la graine de rotation ». Ils ont également fourni une traduction en Python
import math
import drawsvg as draw
class Random:
def __init__(self, seed):
self.JI = seed
def next(self, JA, JE):
self.JI = (self.JI * 5) % 2147483648
return self.JI / 2147483648 * (JE-JA) + JA
def draw_square(g, x, y, i, r1, r2):
r = 5 * 1.4142
pi = 3.14159
move_limit = 5 * i / 264
twist_limit = pi/4 * i / 264
y_center = y + 5 + r1.next(-move_limit, move_limit)
x_center = x + 5 + r1.next(-move_limit, move_limit)
angle = r2.next(pi/4 - twist_limit, pi/4 + twist_limit)
p = draw.Path()
p.M(x_center + r * math.sin(angle), y_center + r * math.cos(angle))
for step in range(4):
angle += pi / 2
p.L(x_center + r * math.sin(angle), y_center + r * math.cos(angle))
g.append(p)
def draw_plot(x_size, y_size, x_count, y_count, s1, s2):
r1 = Random(s1)
r2 = Random(s2)
d = draw.Drawing(180, 280, origin='center', style="background-color:#eae6e2")
g = draw.Group(stroke='#41403a', stroke_width='0.4', fill='none',
stroke_linecap="round", stroke_linejoin="round")
y = -y_size * y_count * 0.5
x0 = -x_size * x_count * 0.5
i = 0
for _ in range(y_count):
x = x0
for _ in range(x_count):
draw_square(g, x, y, i, r1, r2)
x += x_size
i += 1
y += y_size
d.append(g)
return d
d = draw_plot(10.0, 10.0, 12, 22, 1922110153, 1769133315).set_render_size(w=500)
print(d.as_svg())
Je voulais voir si je pouvais aussi traduire cela en R – base plot Je peux très bien dessiner des segments de ligne, et j’étais curieux de colorer les carrés à ma manière.
La plupart de ce code se traduit simplement, à l’exception du fait que le « caractère aléatoire » est en réalité une séquence de valeurs, commençant par une graine spécifique. J’ai récemment parlé d’un de mes anciens articles qui utilise (abusivement) le set.seed() fonction pour générer des mots “aléatoires” spécifiques
printStr <- function(str) paste(str, collapse="") set.seed(2505587); x <- sample(LETTERS, 5, replace=TRUE) set.seed(11135560);y <- sample(LETTERS, 5, replace=TRUE) paste(printStr(x), printStr(y)) ## [1] "HELLO WORLD"
que j’ai eu envie de revisiter sur la base d’un article d’Andrew Heiss.
Le Random la classe dans cette traduction Python produit un itérateur qui renvoie une valeur « suivante » à chaque fois qu’elle est appelée avec une « graine » spécifique et deux valeurs
class Random:
def __init__(self, seed):
self.JI = seed
def next(self, JA, JE):
self.JI = (self.JI * 5) % 2147483648
return self.JI / 2147483648 * (JE-JA) + JA
r = Random(1)
r.next(2, 3)
## 2.0000000023283064
r.next(2, 3)
## 2.000000011641532
r.next(2, 3)
## 2.000000058207661
avec la complexité supplémentaire que les appels ultérieurs mettre à jour la graine elle-même.
Quand j’ai vu cela pour la première fois, mon esprit est revenu à la lecture de l’article R « original » « R : A Language for Data Analysis and Graphics » de Ross Ihaka et Robert Gentleman, dans lequel je me souviens avoir vu l’exemple sympa d’un système OO maintenant un état (non global) via <<-

Maintenir l’état du solde total interne à la fonction
Avec cette même astuce, nous pouvons écrire un équivalent de Random classe qui met également à jour la graine en interne
random <- function(seed) {
list(
nextval = function(a, b) {
seed <<- (seed * 5) %% 2147483648
seed / 2147483648 * (b-a) + a
}
)
}
r <- random(1)
print(r$nextval(2, 3), digits = 16)
## [1] 2.000000002328306
print(r$nextval(2, 3), digits = 16)
## [1] 2.000000011641532
print(r$nextval(2, 3), digits = 16)
## [1] 2.000000058207661
Cool!
Le reste de la traduction s’aligne principalement sur la syntaxe du tracé de base.
C’est avec ça que j’ai fini avec
draw_square <- function(x, y, i, r1, r2, col) {
r = 5 * 1.4142
move_limit = 5 * i / 264
twist_limit = pi/4 * i / 264
y_center = y + 5 + r1$nextval(-move_limit, move_limit)
x_center = x + 5 + r1$nextval(-move_limit, move_limit)
angle = r2$nextval(pi/4 - twist_limit, pi/4 + twist_limit)
x0 <- x_center + r * sin(angle)
y0 <- y_center + r * cos(angle)
for (step in 1:4) {
angle <- angle + pi / 2
x1 <- x_center + r * sin(angle)
y1 <- y_center + r * cos(angle)
segments(x0, y0, x1, y1, lwd = 1.75, col = col)
x0 <- x1
y0 <- y1
}
}
draw_plot <- function(x_size, y_size, x_count, y_count, s1, s2) {
r1 = random(s1)
r2 = random(s2)
plot(NULL, NULL, xlim = c(-60, 60), ylim = c(120, -120), axes = FALSE, ann = FALSE)
y = -y_size * y_count * 0.5
x0 = -x_size * x_count * 0.5
i = 0
for (z in 1:y_count) {
x = x0
for (zz in 1:x_count) {
draw_square(x, y, i, r1, r2, "black")
x <- x + x_size
i <- i + 1
}
y <- y + y_size
}
}
draw_plot(10.0, 10.0, 12, 22, 1922110153, 1769133315)

Figure 1 : « Gravier » dans R
Qui utilise les graines spéciales découvertes dans ce message original. En vérifiant les rotations, cela semble effectivement correspondre à l’art original.
Mais pourquoi s’arrêter là ? Maintenant que je peux le tracer, je peux changer les choses… et si j’utilisais un ensemble de graines différent, par exemple en les échangeant ?
draw_plot(10.0, 10.0, 12, 22, 1769133315, 1922110153)

Figure 2 : ‘Schotter’ dans R avec graines échangées
ou des valeurs complètement différentes ?
draw_plot(10.0, 10.0, 12, 22, 12345, 67890)

Figure 3 : ‘Schotter’ en R avec de nouvelles graines
Et si on changeait les couleurs ? Je pourrais tracer la couleur en fonction de la progression sur la grille, ce qui me semble plutôt cool.
draw_plot <- function(x_size, y_size, x_count, y_count, s1, s2) {
r1 = random(s1)
r2 = random(s2)
plot(NULL, NULL, xlim = c(-60, 60), ylim = c(120, -120), axes = FALSE, ann = FALSE)
y = -y_size * y_count * 0.5
x0 = -x_size * x_count * 0.5
i = 0
for (z in 1:y_count) {
x = x0
rcol <- scales::viridis_pal(option = "viridis")(y_count)[z]
for (zz in 1:x_count) {
draw_square(x, y, i, r1, r2, rcol)
x <- x + x_size
i <- i + 1
}
y <- y + y_size
}
}
draw_plot(10.0, 10.0, 12, 22, 1922110153, 1769133315)

Figure 4 : ‘Schotter’ en R avec les couleurs viridis
Depuis que j’ai rédigé cet article, j’ai vu d’autres exemples de travaux similaires. Cet outil a démontré une version simplifiée
suppressPackageStartupMessages(library(tidyverse))
crossing(x=0:10, y=x) |>
mutate(dx = rnorm(n(), 0, (y/20)^1.5),
dy = rnorm(n(), 0, (y/20)^1.5)) |>
ggplot() +
geom_tile(aes(x=x+dx, y=y+dy, fill=y), colour='black',
lwd=2, width=1, height=1, alpha=0.8, show.legend=FALSE) +
scale_fill_gradient(high='#9f025e', low='#f9c929') +
scale_y_reverse() + theme_void()
tandis que celui-ci montrait un livre ‘Crisis Engineering’ avec une idée similaire

Couverture de « Ingénierie de crise »
Je suis sûr que j’en ai vu d’autres aussi.
C’était une exploration amusante d’une traduction de code d’inspiration artistique, et j’ai pu étirer un peu mes muscles de « maintien de l’état interne ». Je n’ai aucun doute que quelqu’un de plus artistique que moi pourrait en faire beaucoup plus.
Comme toujours, je peux être trouvé sur Mastodon et dans la section commentaires ci-dessous.
devtools :: session_info ()
## ─ Session info ─────────────────────────────────────────────────────────────── ## setting value ## version R version 4.5.3 (2026-03-11) ## os macOS Tahoe 26.3.1 ## system aarch64, darwin20 ## ui X11 ## language (EN) ## collate en_US.UTF-8 ## ctype en_US.UTF-8 ## tz Australia/Adelaide ## date 2026-04-17 ## pandoc 3.6.3 @ /Applications/RStudio.app/Contents/Resources/app/quarto/bin/tools/aarch64/ (via rmarkdown) ## quarto 1.7.31 @ /usr/local/bin/quarto ## ## ─ Packages ─────────────────────────────────────────────────────────────────── ## package * version date (UTC) lib source ## blogdown 1.23 2026-01-18 [1] CRAN (R 4.5.2) ## bookdown 0.46 2025-12-05 [1] CRAN (R 4.5.2) ## bslib 0.10.0 2026-01-26 [1] CRAN (R 4.5.2) ## cachem 1.1.0 2024-05-16 [1] CRAN (R 4.5.0) ## cli 3.6.5 2025-04-23 [1] CRAN (R 4.5.0) ## devtools 2.4.6 2025-10-03 [1] CRAN (R 4.5.0) ## digest 0.6.39 2025-11-19 [1] CRAN (R 4.5.2) ## dplyr * 1.2.0 2026-02-03 [1] CRAN (R 4.5.2) ## ellipsis 0.3.2 2021-04-29 [1] CRAN (R 4.5.0) ## evaluate 1.0.5 2025-08-27 [1] CRAN (R 4.5.0) ## farver 2.1.2 2024-05-13 [1] CRAN (R 4.5.0) ## fastmap 1.2.0 2024-05-15 [1] CRAN (R 4.5.0) ## forcats * 1.0.1 2025-09-25 [1] CRAN (R 4.5.0) ## fs 1.6.7 2026-03-06 [1] CRAN (R 4.5.2) ## generics 0.1.4 2025-05-09 [1] CRAN (R 4.5.0) ## ggplot2 * 4.0.2 2026-02-03 [1] CRAN (R 4.5.2) ## glue 1.8.0 2024-09-30 [1] CRAN (R 4.5.0) ## gtable 0.3.6 2024-10-25 [1] CRAN (R 4.5.0) ## hms 1.1.4 2025-10-17 [1] CRAN (R 4.5.0) ## htmltools 0.5.9 2025-12-04 [1] CRAN (R 4.5.2) ## jquerylib 0.1.4 2021-04-26 [1] CRAN (R 4.5.0) ## jsonlite 2.0.0 2025-03-27 [1] CRAN (R 4.5.0) ## knitr 1.51 2025-12-20 [1] CRAN (R 4.5.2) ## labeling 0.4.3 2023-08-29 [1] CRAN (R 4.5.0) ## lattice 0.22-9 2026-02-09 [1] CRAN (R 4.5.3) ## lifecycle 1.0.5 2026-01-08 [1] CRAN (R 4.5.2) ## lubridate * 1.9.5 2026-02-04 [1] CRAN (R 4.5.2) ## magrittr 2.0.4 2025-09-12 [1] CRAN (R 4.5.0) ## Matrix 1.7-4 2025-08-28 [1] CRAN (R 4.5.3) ## memoise 2.0.1 2021-11-26 [1] CRAN (R 4.5.0) ## otel 0.2.0 2025-08-29 [1] CRAN (R 4.5.0) ## pillar 1.11.1 2025-09-17 [1] CRAN (R 4.5.0) ## pkgbuild 1.4.8 2025-05-26 [1] CRAN (R 4.5.0) ## pkgconfig 2.0.3 2019-09-22 [1] CRAN (R 4.5.0) ## pkgload 1.5.0 2026-02-03 [1] CRAN (R 4.5.2) ## png 0.1-9 2026-03-15 [1] CRAN (R 4.5.2) ## purrr * 1.2.1 2026-01-09 [1] CRAN (R 4.5.2) ## R6 2.6.1 2025-02-15 [1] CRAN (R 4.5.0) ## RColorBrewer 1.1-3 2022-04-03 [1] CRAN (R 4.5.0) ## Rcpp 1.1.1 2026-01-10 [1] CRAN (R 4.5.2) ## readr * 2.2.0 2026-02-19 [1] CRAN (R 4.5.2) ## remotes 2.5.0 2024-03-17 [1] CRAN (R 4.5.0) ## reticulate 1.45.0 2026-02-13 [1] CRAN (R 4.5.2) ## rlang 1.1.7 2026-01-09 [1] CRAN (R 4.5.2) ## rmarkdown 2.30 2025-09-28 [1] CRAN (R 4.5.0) ## rstudioapi 0.18.0 2026-01-16 [1] CRAN (R 4.5.2) ## S7 0.2.1 2025-11-14 [1] CRAN (R 4.5.2) ## sass 0.4.10 2025-04-11 [1] CRAN (R 4.5.0) ## scales 1.4.0 2025-04-24 [1] CRAN (R 4.5.0) ## sessioninfo 1.2.3 2025-02-05 [1] CRAN (R 4.5.0) ## stringi 1.8.7 2025-03-27 [1] CRAN (R 4.5.0) ## stringr * 1.6.0 2025-11-04 [1] CRAN (R 4.5.0) ## tibble * 3.3.1 2026-01-11 [1] CRAN (R 4.5.2) ## tidyr * 1.3.2 2025-12-19 [1] CRAN (R 4.5.2) ## tidyselect 1.2.1 2024-03-11 [1] CRAN (R 4.5.0) ## tidyverse * 2.0.0 2023-02-22 [1] CRAN (R 4.5.0) ## timechange 0.4.0 2026-01-29 [1] CRAN (R 4.5.2) ## tzdb 0.5.0 2025-03-15 [1] CRAN (R 4.5.0) ## usethis 3.2.1 2025-09-06 [1] CRAN (R 4.5.0) ## vctrs 0.7.1 2026-01-23 [1] CRAN (R 4.5.2) ## viridisLite 0.4.3 2026-02-04 [1] CRAN (R 4.5.2) ## withr 3.0.2 2024-10-28 [1] CRAN (R 4.5.0) ## xfun 0.56 2026-01-18 [1] CRAN (R 4.5.2) ## yaml 2.3.12 2025-12-10 [1] CRAN (R 4.5.2) ## ## [1] /Library/Frameworks/R.framework/Versions/4.5-arm64/Resources/library ## * ── Packages attached to the search path. ## ## ─ Python configuration ─────────────────────────────────────────────────────── ## python: /Users/jono/.cache/uv/archive-v0/3n3euDImmjsw3EYTJjfeY/bin/python ## libpython: /Users/jono/.local/share/uv/python/cpython-3.12.12-macos-aarch64-none/lib/libpython3.12.dylib ## pythonhome: /Users/jono/.cache/uv/archive-v0/3n3euDImmjsw3EYTJjfeY:/Users/jono/.cache/uv/archive-v0/3n3euDImmjsw3EYTJjfeY ## virtualenv: /Users/jono/.cache/uv/archive-v0/3n3euDImmjsw3EYTJjfeY/bin/activate_this.py ## version: 3.12.12 (main, Oct 28 2025, 11:52:25) [Clang 20.1.4 ] ## numpy: /Users/jono/.cache/uv/archive-v0/3n3euDImmjsw3EYTJjfeY/lib/python3.12/site-packages/numpy ## numpy_version: 2.4.4 ## ## NOTE: Python version was forced by VIRTUAL_ENV ## ## ──────────────────────────────────────────────────────────────────────────────
En rapport
PakarPBN
A Private Blog Network (PBN) is a collection of websites that are controlled by a single individual or organization and used primarily to build backlinks to a “money site” in order to influence its ranking in search engines such as Google. The core idea behind a PBN is based on the importance of backlinks in Google’s ranking algorithm. Since Google views backlinks as signals of authority and trust, some website owners attempt to artificially create these signals through a controlled network of sites.
In a typical PBN setup, the owner acquires expired or aged domains that already have existing authority, backlinks, and history. These domains are rebuilt with new content and hosted separately, often using different IP addresses, hosting providers, themes, and ownership details to make them appear unrelated. Within the content published on these sites, links are strategically placed that point to the main website the owner wants to rank higher. By doing this, the owner attempts to pass link equity (also known as “link juice”) from the PBN sites to the target website.
The purpose of a PBN is to give the impression that the target website is naturally earning links from multiple independent sources. If done effectively, this can temporarily improve keyword rankings, increase organic visibility, and drive more traffic from search results.