Unreal Engine sur Macbook M2

Si vous avez pu exécuter Unreal Engine 5 Editor sur un Macbook équipé d’un processeur Apple, vous avez peut-être remarqué que cette chose est assez lente.

Pour augmenter les performances de l’éditeur et du moteur, définissez les paramètres d’évolutivité du moteur -> Moyen. Après cela, le moteur commencera à tout dessiner de manière moins belle, mais vous pourrez travailler normalement avec le moteur de votre MacBook.

Correction du menu mobile dans WordPress


document.addEventListener('DOMContentLoaded', function() {
    new navMenu('primary');
    new navMenu('woo');
});

Si vous n’avez pas non plus ouvert le menu du blog sur iOS/Android depuis plusieurs années sur votre blog WordPress lorsque vous utilisez le thème Seedlet, alors ajoutez simplement :
Fermeture du fichier wp-content/themes/seedlet/assets/js/primary-navigation.js dans la fonction, à côté de la fenêtre d’abonnement par défaut addEventListener ‘load’.

Radio-Maximum-Electron

Radio Maximum Electron est une application puissante et pratique conçue pour écouter le flux de la station de radio Radio Maximum sur votre ordinateur exécutant les systèmes d’exploitation Windows, Linux et macOS. Ce lecteur allie facilité d’utilisation et fonctionnalités élevées, vous donnant accès à la diffusion en direct avec un minimum d’effort.

Téléchargez simplement l’application depuis GitHub :

https://github.com/demensdeum/Radio-Maximum-Electron/releases

L’auteur n’a rien à voir avec Radio Maximum, il aime juste beaucoup cette radio.
La fonctionnalité principale est implémentée par le projet Nativifier

https://github.com/nativefier/nativefier

Licence pour les scripts de build MIT, le runtime a sa propre licence !

Aventure sous-marine avec l’ours polaire

Un jeu simple avec des labyrinthes générés à l’infini utilisant ThreeJS.

Créé dans le cadre d’un game jam de 3 jours “Start the Game” sur le thème “Family Game”.

Un petit explorateur polaire marchait sur la glace avec sa mère lorsqu’un désastre s’est produit : la glace s’est fissurée et il est tombé dans les eaux glacées de l’océan. Maman n’a pas eu le temps de le sauver et l’ours s’est retrouvé dans une mystérieuse grotte sous-marine. À sa grande surprise, il découvre qu’il peut respirer sous l’eau. Il n’y a qu’une seule façon de sortir de ce piège : surmonter les profondeurs de la mer, résoudre des énigmes et combattre des requins agressifs, qui peuvent être combattus avec des lancers de pommes bien ciblés.

Son objectif est désormais de trouver un moyen de sortir de ce piège sous-marin et de retourner auprès de sa mère, en surmontant les dangers des profondeurs marines et en résolvant des énigmes.

https://demensdeum.com/demos/arctica/

Nixy Player

Nixy Player – Petit environnement d’exécution JavaScript extensible et multiplateforme.

Multiplateforme : disponible sur Windows, macOS et Linux, ainsi que sur toute autre plateforme prenant en charge le C++ et les bibliothèques dynamiques.
Léger : consommation minimale de ressources avec des performances efficaces.
Extensible : conçu pour être facilement étendu avec des plugins et des bibliothèques supplémentaires.

Veuillez visiter la page des versions pour les dernières versions et mises à jour :
https://github.com/demensdeum/NixyPlayer/releases/

Raiden Video Ripper

Raiden Video Ripper est un projet open source conçu pour le montage vidéo et la conversion de format. Il est construit à l’aide de Qt 6 (Qt Creator) et vous permet de découper et de convertir des vidéos aux formats MP4, GIF et WebM. Vous pouvez également extraire l’audio des vidéos et le convertir au format MP3.
Интерфейс RaidenVideoRipper

Photo du COSTA RICA EN 4K 60fps HDR (ULTRA HD)
https://www.youtube.com/watch?v=LXb3EKWsInQ
Veuillez visiter la page des versions pour rester informé des dernières versions et mises à jour :
https://github.com/demensdeum/RaidenVideoRipper/releases

Donki Hills

En un mois j’ai réalisé un drôle de gag, un jeu parodique utilisant l’Unreal Engine 5. Le développement a été réalisé sur le stream Twitch.

L’histoire de ce jeu raconte l’histoire d’un Russe ordinaire, James, qui a trouvé une fille, Maria, sur Tinder, mais en raison des sanctions et de la suppression de Tinder en Russie, il a perdu le contact avec elle. Il ne lui reste plus qu’une capture d’écran de sa photo. À l’aide de Google Maps, il trouve l’endroit où la photo a été prise – le village de Tikhie Donki près de Novossibirsk. James s’y rend à la recherche de Maria…

https://demensdeum.itch.io/donki-hills

Robots défenseurs

Très souvent, lors de discussions sur le bon fonctionnement de certaines fonctionnalités du logiciel, je tombe sur une situation où la fonctionnalité du point de vue de l’utilisateur semble étrange et illogique. La discussion avec le propriétaire du produit ressemblait à ceci :

– Il y a clairement un problème de comportement ici
– Eh bien, nous le publierons et lorsque les utilisateurs commenceront à se plaindre, nous le corrigerons
– ??? Eh bien, d’accord…

Cela semble être un programme efficace, n’est-ce pas ? Un algorithme assez optimal pour les équipes avec un petit budget, des délais serrés, une recherche insuffisante/manque de spécialiste UI/UX. Les utilisateurs se plaindront si quelque chose arrive, ce n’est pas grave.
Une recherche Google révèle que la source de cette méthode provient d’un article – « Développement axé sur les plaintes » par Coding Horror

Une fois, je vendais de la nourriture, y compris des saucisses du docteur, pour 300 roubles. via un terminal dans un supermarché, j’ai quitté le magasin avec cette saucisse en toute confiance qu’elle avait été payée – le terminal m’a proposé de ne pas imprimer le chèque et j’ai accepté pour ne pas gaspiller du précieux papier sur ce chèque. Pendant le processus de « poinçonnage » des marchandises pour chaque produit, le terminal a émis un grincement, signalant que tout a fonctionné correctement. De plus, avec une alerte sonore, le terminal a fait un clin d’œil avec le rétroéclairage du lecteur de codes-barres.

Le lendemain, je suis allé au supermarché pour faire à nouveau des courses et j’ai déposé les courses dans le terminal. À la sortie, j’ai été accueilli par un homme d’apparence sudiste avec une barbe épaisse, me tendant un smartphone, il a dit : « ; “C’est toi devant la caméra ?”, j’ai regardé son téléphone et je me suis vu dans un T-shirt Melodic-Death-Metal d’Arch Enemy avec des crânes et tout ça, il n’y avait aucune raison d’en douter.
“Oui, c’est moi, qu’est-ce qu’il y a ?”, l’homme, en plissant les yeux très fort, dit : “Hier, tu n’as pas frappé la saucisse.” wow

Après une brève enquête pour savoir qui il était et comment il a tiré ces conclusions, il m’a montré une vidéo accrochée au plafond du magasin, dans la vidéo je frappe la saucisse, le terminal clignote avec le rétroéclairage du scanner, J’ai mis la saucisse dans le sac.

– La vidéo montre comment fonctionnait le scanner
– Cela n’a fonctionné pour rien, payez la saucisse !

Un peu déconcerté par cette attitude, j’ai demandé un cahier de réclamation pour écrire que le terminal avait besoin d’améliorations logicielles, car il donnait tous les signes d’un bon fonctionnement, mais en réalité il était simplement buggé, sans le signaler à l’écran de quelque manière que ce soit.

Après 10 minutes de querelles avec lui et son patron, qui a immédiatement pris la défense de son employé et du terminal qui fonctionnait pour rien, ils ont décidé d’appeler la fille de l’administrateur pour qu’elle apporte un cahier de plaintes et frappe le médecin. saucisse.

Ce jour-là, j’ai réalisé à quel point il est difficile pour les utilisateurs de se plaindre des produits matériels et logiciels, et que le mantra « les gens se plaindront » est très probable. Réparons-le » fonctionne très mal. La raison principale, ce sont les gens qui défendent les robots cassés, les solutions logicielles cassées, pour plus de simplicité, je propose d’introduire de nouveaux termes – ” ; Défenseur du robot brisé et défenseur des systèmes brisés.

Les utilisateurs ordinaires ne peuvent pas se plaindre du dysfonctionnement des terminaux car ils sont dérangés par les ZasRoshniks, qui, pour une raison quelconque, s’attachent et commencent à aimer les machines avec lesquelles ils travaillent, les considérant peut-être comme des sortes d’entités animées, oubliant qu’il n’y a rien. y vivre.< /p>

Une situation similaire se produit avec ZaSSoshniki, ces personnes peuvent écumer à la bouche pour défendre certaines lacunes stupides des frameworks, des langages de programmation ou de tout autre produit logiciel, malgré les plaintes des utilisateurs et d’autres développeurs.
Une conversation typique avec ZaSSoshnik est la suivante :

– Ici quelque chose ne fonctionne pas, d’après la documentation tout semble correct
– Oh, donc vous n’avez pas lu ce manuel de 2005, où en bas il est écrit en minuscules que vous devez ajouter PROGRAM_START:6969
– ??? euh

Ces personnes peuvent ne pas comprendre comment elles contribuent elles-mêmes à la propagation des problèmes, des erreurs, de la perte de temps et d’argent de leur propre chef et de celle des autres. À cause d’eux, tout le monde en souffre, car la transformation numérique est impossible si les choses non évidentes et les problèmes liés aux solutions logicielles et matérielles sont étouffés.
Je suis au courant de l’histoire récente d’un bug dans le logiciel Horizon de la Poste britannique qui a poussé les gens à s’endetter, ruiné les mariages et ruiné la vie des gens pendant des décennies. Tout cela a continué grâce à la connivence de personnes qui ont gardé le silence sur les problèmes du logiciel, « protégeant » ainsi l’entreprise. lui.

Mes amis, ne soyez pas des ZaSRoshniks et des ZaSSoshniks, traitez les outils avec lesquels vous travaillez avec un grain de sel, sinon vous serez confrontés à un asservissement total à des systèmes merdiques et cassés, comme des otages dans le nouveau monde numérique du futur. Pour ceux qui ne le peuvent pas – au moins, ne dérangez pas les autres personnes qui essaient de prêter attention aux logiciels/matériels qui ne fonctionnent pas et qui interfèrent, car les développeurs de ces produits ont convenu « » “Lorsque les utilisateurs commenceront à se plaindre, nous y remédierons.”

Sources
https://blog.codinghorror.com/complaint-driven-development/< /a>
https://habr.com/ru/articles/554404/< br />
https://en.wikipedia.org/wiki/British_Post_Office_scandal

Création de l’application bgfx Emscripten

Dans cet article, je décrirai une manière de créer des applications bgfx pour le Web (WebAssembly) à l’aide d’Emscripten.

La plate-forme d’installation est Linux x86-64, par exemple Arch Linux.

Tout d’abord, installons Emscripten version 3.1.51, sinon vous ne réussirez pas, tout cela à cause du changement de type de bibliothèques dynamiques dans la dernière version d’Emscripten. Vous pouvez en savoir plus ici :
https://github.com/bkaradzic/bgfx/discussions/3266

Cela se fait comme ceci :


git clone https://github.com/emscripten-core/emsdk.git



cd emsdk



./emsdk install 3.1.51



./emsdk activate 3.1.51



source ./emsdk_env.sh



Assemblons bgfx pour WebAssembly – Texte :


mkdir bgfx-build-test



cd bgfx-build-test



git clone https://github.com/bkaradzic/bx.git



git clone https://github.com/bkaradzic/bimg.git



git clone https://github.com/bkaradzic/bgfx.git



cd bgfx



emmake make wasm-debug



Par conséquent, dans le dossier .build, vous aurez des fichiers bitcode avec une extension .bc, qui devront être liés à votre application bgfx.
Doit être bgfx.bc, bx.bc, bimg.bc ; différents assemblys ont des noms différents pour ces fichiers, selon le type d’assembly (version/débogage)

Ajoutez un lien vers le fichier CMakeLists.txt avec les fichiers .bc, par exemple, les chemins absolus vers les fichiers du projet bgfx-experiments :


target_link_libraries(${PROJECT_NAME} SDL2 GL /home/demensdeum_stream/Sources/bgfx-build/bgfx/.build/wasm/bin/bgfxDebug.bc /home/demensdeum_stream/Sources/bgfx-build/bgfx/.build/wasm/bin/bxDebug.bc /home/demensdeum_stream/Sources/bgfx-build/bgfx/.build/wasm/bin/bimgDebug.bc)



Maintenant, remplacez le handle de fenêtre natif dans les données de la plateforme par l’initialisation bgfx :


bgfx::PlatformData platformData{};



platformData.context = NULL;



platformData.backBuffer = NULL;



platformData.backBufferDS = NULL;



platformData.nwh = (void*)"#canvas";



Vous devez également changer le type de rendu en OpenGL :


bgfx::Init init;



init.type = bgfx::RendererType::OpenGL;







init.resolution.width = screenWidth;



init.resolution.height = screenHeight;



init.resolution.reset = BGFX_RESET_VSYNC;



init.platformData = platformData;







if (!bgfx::init(init))



{



    throw std::runtime_error("Failed to initialize bgfx");



}



Recompiler les shaders GLSL à 120 :


shaderc -f "VertexShader.vs" -o "VertexShader.glsl" --type "v" -p "120"



shaderc -f "FragmentShader.fs" -o "FragmentShader.glsl" --type "f" -p "120"



Oui, mais les fichiers .glsl doivent être ajoutés à CMakeLists.txt en tant que « fichier de préchargement :


set(CMAKE_CXX_FLAGS ... <Остальная часть>



--preload-file VertexShader.glsl \



--preload-file FragmentShader.glsl \



Il reste à remplacer la boucle de rendu principale de votre application par un appel de fonction while via emscripten_set_main_loop.

Vous pouvez en savoir plus ici :
https https://demensdeum.com/blog/ru/2017/03/29/porting-sdl-c-game-to-html5-emscripten/

Ensuite, assemblez votre projet Emscripten comme d’habitude, tout devrait fonctionner.
Intéressant – la version Emscripten 3.1.51 semble manquer d’OpenAL (ou juste de moi).

Code source du projet qui se compile correctement avec bgfx et Emscripten :
https://github.com/demensdeum/ bgfx-experiments/tree/main/2-emscripten-build

Sources

https://github.com/bkaradzic/bgfx/discussions/3266
https://bkaradzic.github.io/bgfx/build.html
https://emscripten.org/docs/getting_started/downloads.html
https ://demensdeum.com/blog/ru/2017/03/29/porting-sdl-c-game-to-html5-emscripten/
https://llvm.org/docs/BitCodeFormat.html

Portage du Surreal Engine C++ vers WebAssembly

Dans cet article, je décrirai comment j’ai porté le moteur de jeu Surreal Engine vers WebAssembly.

Moteur surréaliste &#8211 ; un moteur de jeu qui implémente la plupart des fonctionnalités de l’Unreal Engine 1, des jeux célèbres sur ce moteur – Unreal Tournament 99, Unreal, Deus Ex, Undying. Il fait référence aux moteurs classiques qui fonctionnaient principalement dans un environnement d’exécution monothread.

Au départ, j’ai eu l’idée de me lancer dans un projet que je ne pouvais pas réaliser dans un délai raisonnable, montrant ainsi à mes abonnés Twitch qu’il y a des projets que même moi je ne peux pas réaliser. Lors de mon premier stream, j’ai soudainement réalisé que la tâche de porter Surreal Engine C++ vers WebAssembly à l’aide d’Emscripten était réalisable.

Surreal Engine Emscripten Demo

Après un mois, je peux faire une démonstration de mon ensemble fourche et moteur sur WebAssembly :
https://demensdeum.com/demos/SurrealEngine/

Le contrôle, comme dans l’original, s’effectue à l’aide des flèches du clavier. Ensuite, je prévois de l’adapter au contrôle mobile (tachi), en ajoutant un éclairage correct et d’autres fonctionnalités graphiques du rendu Unreal Tournament 99.

Par où commencer ?

La première chose que je veux dire est que n’importe quel projet peut être porté de C++ vers WebAssembly en utilisant Emscripten, la seule question est de savoir dans quelle mesure la fonctionnalité sera complète. Choisissez un projet dont les ports de bibliothèque sont déjà disponibles pour Emscripten ; dans le cas de Surreal Engine, vous avez beaucoup de chance, car le moteur utilise les bibliothèques SDL 2, OpenAL – ils sont tous deux portés sur Emscripten. Cependant, Vulkan est utilisé comme API graphique, qui n’est actuellement pas disponible pour HTML5, des travaux sont en cours pour implémenter WebGPU, mais il est également au stade de projet, et on ne sait pas non plus à quel point le port ultérieur de Vulkan vers WebGPU sera simple. , une fois qu’il est entièrement standardisé. Par conséquent, j’ai dû écrire mon propre rendu de base OpenGL-ES/WebGL pour Surreal Engine.

Construire le projet

Construire un système dans Surreal Engine – CMake, qui simplifie également le portage, car Emscripten fournit à ses constructeurs natifs – emcmake, emmake.
Le portage de Surreal Engine était basé sur le code de mon dernier jeu en WebGL/OpenGL ES et C++ appelé Death-Mask, de ce fait le développement était beaucoup plus simple, j’avais tous les indicateurs de build nécessaires avec moi et des exemples de code.

L’un des points les plus importants de CMakeLists.txt concerne les indicateurs de build pour Emscripten. Vous trouverez ci-dessous un exemple tiré du fichier de projet :


-s MAX_WEBGL_VERSION=2 \

-s EXCEPTION_DEBUG \

-fexceptions \

--preload-file UnrealTournament/ \

--preload-file SurrealEngine.pk3 \

--bind \

--use-preload-plugins \

-Wall \

-Wextra \

-Werror=return-type \

-s USE_SDL=2 \

-s ASSERTIONS=1 \

-w \

-g4 \

-s DISABLE_EXCEPTION_CATCHING=0 \

-O3 \

--no-heap-copy \

-s ALLOW_MEMORY_GROWTH=1 \

-s EXIT_RUNTIME=1")

Le script de build lui-même :


emmake make -j 16

cp SurrealEngine.data /srv/http/SurrealEngine/SurrealEngine.data

cp SurrealEngine.js /srv/http/SurrealEngine/SurrealEngine.js

cp SurrealEngine.wasm /srv/http/SurrealEngine/SurrealEngine.wasm

cp ../buildScripts/Emscripten/index.html /srv/http/SurrealEngine/index.html

cp ../buildScripts/Emscripten/background.png /srv/http/SurrealEngine/background.png

Ensuite, nous préparerons l’index .html , qui inclut le préchargeur du système de fichiers du projet. Pour télécharger sur le Web, j’ai utilisé Unreal Tournament Demo version 338. Comme vous pouvez le voir sur le fichier CMake, le dossier du jeu décompressé a été ajouté au répertoire de construction et lié en tant que fichier de préchargement pour Emscripten.

Modifications du code principal

Ensuite, il a fallu changer la boucle de jeu du jeu, vous ne pouvez pas exécuter une boucle sans fin, cela conduit au gel du navigateur, vous devez plutôt utiliser emscripten_set_main_loop, j’ai écrit à propos de cette fonctionnalité dans ma note de 2017 « < a href="https://demensdeum.com /blog/ru/2017/03/29/porting-sdl-c-game-to-html5-emscripten/" rel="noopener" target="_blank">Porter le jeu SDL C++ vers HTML5 (Emscripten)”
Nous modifions le code pour quitter la boucle while en if, puis nous affichons la classe principale du moteur de jeu, qui contient la boucle de jeu, dans la portée globale, et écrivons une fonction globale qui appellera l’étape de boucle de jeu à partir de l’objet global :


#include <emscripten.h>

Engine *EMSCRIPTEN_GLOBAL_GAME_ENGINE = nullptr;

void emscripten_game_loop_step() {

	EMSCRIPTEN_GLOBAL_GAME_ENGINE->Run();

}

#endif

Après cela, vous devez vous assurer qu’il n’y a pas de threads d’arrière-plan dans l’application ; s’il y en a, préparez-vous à les réécrire pour une exécution monothread, ou utilisez la bibliothèque phtread dans Emscripten.
Le fil d’arrière-plan dans Surreal Engine est utilisé pour lire de la musique, les données proviennent du fil du moteur principal concernant la piste en cours, la nécessité de jouer de la musique ou son absence, puis le fil d’arrière-plan reçoit un nouvel état via un mutex et commence à jouer de la nouvelle musique. , ou le met en pause. Le flux d’arrière-plan est également utilisé pour mettre la musique en mémoire tampon pendant la lecture.
Mes tentatives de création de Surreal Engine pour Emscripten avec pthread ont échoué, car les ports SDL2 et OpenAL ont été construits sans le support de pthread, et je ne voulais pas les reconstruire pour le plaisir de la musique. Par conséquent, j’ai transféré la fonctionnalité du flux de musique de fond vers une exécution monothread à l’aide d’une boucle. En supprimant les appels pthread du code C++, j’ai déplacé la mise en mémoire tampon et la lecture de musique vers le thread principal, afin qu’il n’y ait pas de retard, j’ai augmenté la mémoire tampon de quelques secondes.

Ensuite, je décrirai des implémentations spécifiques des graphiques et du son.

Vulkan n’est pas pris en charge !

Oui, Vulkan n’est pas pris en charge en HTML5, bien que toutes les brochures marketing présentent le support multiplateforme et large plate-forme comme le principal avantage de Vulkan. Pour cette raison, j’ai dû écrire mon propre moteur de rendu graphique de base pour un type OpenGL simplifié – – ES, il est utilisé sur les appareils mobiles, parfois il ne contient pas les fonctionnalités à la mode de l’OpenGL moderne, mais il se porte très bien sur WebGL, ce qui est exactement ce qu’Emscripten implémente. L’écriture du rendu de tuiles de base, du rendu bsp, pour l’affichage GUI le plus simple et du rendu des modèles + cartes a été achevée en deux semaines. C’était peut-être la partie la plus difficile du projet. Il reste encore beaucoup de travail à faire pour implémenter toutes les fonctionnalités du rendu Surreal Engine, donc toute aide des lecteurs est la bienvenue sous forme de code et de demandes d’extraction.

OpenAL pris en charge !

La grande chance est que Surreal Engine utilise OpenAL pour la sortie audio. Après avoir écrit un simple hello world dans OpenAL et l’avoir assemblé dans WebAssembly à l’aide d’Emscripten, il m’est devenu clair à quel point tout était simple et j’ai décidé de porter le son.
Après plusieurs heures de débogage, il est devenu évident que l’implémentation OpenAL d’Emscripten avait plusieurs bugs, par exemple, lors de l’initialisation de la lecture du nombre de canaux mono, la méthode a renvoyé un nombre infini, et après avoir essayé d’initialiser un vecteur de taille infinie, C++ plante avec l’exception vector::length_error.
Nous avons réussi à contourner ce problème en codant en dur le nombre de canaux mono à 2 048 :


		alcGetIntegerv(alDevice, ALC_STEREO_SOURCES, 1, &stereoSources);



#if __EMSCRIPTEN__

		monoSources = 2048; // for some reason Emscripten's OpenAL gives infinite monoSources count, bug?

#endif



Y a-t-il un réseau ?

Surreal Engine ne prend actuellement pas en charge le jeu en ligne, jouer avec des robots est pris en charge, mais nous avons besoin de quelqu’un pour écrire l’IA pour ces robots. Théoriquement, vous pouvez implémenter un jeu en réseau sur WebAssembly/Emscripten à l’aide de Websockets.

Conclusion

En conclusion, je voudrais dire que le portage de Surreal Engine s’est avéré assez fluide grâce à l’utilisation de bibliothèques pour lesquelles il existe des ports Emscripten, ainsi que mon expérience passée dans l’implémentation d’un jeu en C++ pour WebAssembly. sur Emscripten. Vous trouverez ci-dessous des liens vers des sources de connaissances et des référentiels sur le sujet.
M-M-M-MONSTER KILL !

De plus, si vous souhaitez aider le projet, de préférence avec le code de rendu WebGL/OpenGL ES, alors écrivez-moi dans Telegram :
https://t.me/demenscave

Liens

https://demensdeum.com/demos/SurrealEngine/
https://github.com/demensdeum/SurrealEngine-Emscripten

https://github.com/dpjudas/SurrealEngine

Flash Forever – Interceptor 2021

Recently, it turned out that Adobe Flash works quite stably under Wine. During a 4-hour stream, I made the game Interceptor 2021, which is a sequel to the game Interceptor 2020, written for the ZX Spectrum.

For those who are not in the know – the Flash technology provided interactivity on the web from 2000 to around 2015. Its shutdown was prompted by an open letter from Steve Jobs, in which he wrote that Flash should be consigned to history because it lagged on the iPhone. Since then, JS has become even more sluggish than Flash, and Flash itself has been wrapped in JS, making it possible to run it on anything thanks to the Ruffle player.

You can play it here:
https://demensdeum.com/demos/Interceptor2021

Video:
https://www.youtube.com/watch?v=-3b5PkBvHQk

Source code:
https://github.com/demensdeum/Interceptor-2021

Jeux de démonstration Masons-DR

Masonry-AR est un jeu de réalité augmentée dans lequel vous devez naviguer dans la ville dans le monde réel et collecter des connaissances maçonniques à partir de livres, obtenir de la monnaie et capturer un territoire pour votre ordre maçonnique. Le jeu n’a aucun rapport avec des organisations réelles, tous les matchs sont aléatoires.

Démo du jeu :
https://demensdeum.com/demos/masonry-ar/client

Vicki :
https://demensdeum.com/masonry-ar-wiki-ru/

Code source :
https://github.com/demensdeum/Masonry-AR

Dépôt CRUD

Dans cette note, je décrirai les principes de base du modèle CRUD classique bien connu, implémenté dans le langage Swift. Swift est un langage ouvert et multiplateforme disponible pour Windows, Linux, macOS, iOS et Android.

Il existe de nombreuses solutions pour extraire le stockage des données et la logique des applications. L’une de ces solutions est l’approche CRUD, qui est l’acronyme de C– Créer, R-Read, U – Mise à jour, D– Supprimer.
Généralement, ce principe est mis en œuvre via la mise en œuvre d’une interface vers la base de données, dans laquelle les éléments sont manipulés à l’aide d’un identifiant unique, tel que id. Une interface est créée pour chaque lettre CRUD – Créer (objet, identifiant), Lire (identifiant), Mettre à jour (objet, identifiant), Supprimer (objet, identifiant).
Si un objet contient un identifiant en lui-même, alors l’argument id peut être omis des méthodes (Create, Update, Delete), puisque l’objet entier y est transmis avec son champ – identifiant. Mais pour – La lecture nécessite un identifiant car nous voulons obtenir un objet de la base de données par identifiant.

Tous les noms sont fictifs

Imaginons qu’une hypothétique application AssistantAI ait été créée à l’aide du SDK de base de données EtherRelm gratuit, l’intégration était simple, l’API était très pratique et, par conséquent, l’application a été lancée sur les marchés.
Du coup, le développeur du SDK EtherRelm décide de le rendre payant, fixant le prix à 100 $ par an et par utilisateur de l’application.
Quoi? Oui! Que doivent faire les développeurs d’AssistantAI maintenant, car ils comptent déjà 1 million d’utilisateurs actifs ! Payer 100 millions de dollars ?
Au lieu de cela, il est décidé d’évaluer le transfert de l’application vers la base de données RootData native de la plateforme ; selon les programmeurs, un tel transfert prendra environ six mois, cela ne prend pas en compte l’implémentation de nouvelles fonctionnalités dans l’application. Après réflexion, il a été décidé de retirer l’application des marchés, de la réécrire sur un autre framework multiplateforme gratuit avec une base de données BueMS intégrée, cela résoudra le problème avec la base de données payante + simplifiera le développement sur d’autres plateformes.
Un an plus tard, l’application a été réécrite dans BueMS, mais soudain, le développeur du framework décide de la rendre payante. Il s’avère que l’équipe est tombée deux fois dans le même piège ; qu’elle parvienne à s’en sortir une deuxième fois est une toute autre histoire.

L’abstraction à la rescousse

Ces problèmes auraient pu être évités si les développeurs avaient utilisé une abstraction des interfaces au sein de l’application. Aux trois piliers de la POO – polymorphisme, encapsulation, héritage, il n’y a pas si longtemps, ils ont ajouté un autre – abstraction.
L’abstraction des données vous permet de décrire des idées et des modèles en termes généraux, avec un minimum de détails, tout en étant suffisamment précis pour mettre en œuvre des implémentations spécifiques utilisées pour résoudre des problèmes commerciaux.
Comment pouvons-nous abstraire le fonctionnement de la base de données afin que la logique de l’application n’en dépende pas ? Nous utilisons l’approche CRUD !

Un diagramme UML CRUD simplifié ressemble à ceci :

Exemple avec une base de données EtherRelm fictive :

Exemple avec une vraie base de données SQLite :

Comme vous l’avez déjà remarqué, lorsque vous changez de base de données, seule celle-ci change ; l’interface CRUD avec laquelle l’application interagit reste inchangée. CRUD est une implémentation du modèle GoF – Adaptateur, parce que en l’utilisant, nous adaptons les interfaces d’application à n’importe quelle base de données et combinons des interfaces incompatibles.
Les mots sont vides, montre-moi le code
Pour implémenter des abstractions dans les langages de programmation, des interfaces/protocoles/classes abstraites sont utilisées. Ce sont tous des phénomènes du même ordre, cependant, lors d’entretiens, on peut vous demander de nommer la différence entre eux, je pense personnellement que cela n’a pas beaucoup de sens car le seul but d’utilisation est de mettre en œuvre l’abstraction des données, sinon il s’agit de tester la mémoire de la personne interrogée.
CRUD est souvent implémenté dans le cadre du modèle Repository, cependant, le référentiel peut ou non implémenter l’interface CRUD, tout dépend de l’ingéniosité du développeur.

Considérons un code Swift assez typique pour le référentiel de structure Book, travaillant directement avec la base de données UserDefaults :


struct Book: Codable {
	let title: String
	let author: String
}

class BookRepository {
	func save(book: Book) {
    		let record = try! JSONEncoder().encode(book)
    		UserDefaults.standard.set(record, forKey: book.title)
	}
    
	func get(bookWithTitle title: String) -> Book? {
    		guard let data = UserDefaults.standard.data(forKey: title) else { return nil }
    		let book = try! JSONDecoder().decode(Book.self, from: data)
    		return book
	}
    
	func delete(book: Book) {
    		UserDefaults.standard.removeObject(forKey: book.title)
	}
}

let book = Book(title: "Fear and Loathing in COBOL", author: "Sir Edsger ZX Spectrum")
let repository = BookRepository()
repository.save(book: book)
print(repository.get(bookWithTitle: book.title)!)
repository.delete(book: book)
guard repository.get(bookWithTitle: book.title) == nil else {
	print("Error: can't delete Book from repository!")
	exit(1)
}

Le code ci-dessus semble simple, mais comptons le nombre de violations du principe DRY (Do not Repeat Yourself) et la cohérence du code :
Connectivité à la base de données UserDefaults
Connectivité avec les encodeurs et décodeurs JSON – JSONEncoder, JSONDecoder
Connecté à la structure Book, mais nous avons besoin d’un référentiel abstrait afin de ne pas créer de classe référentiel pour chaque structure que nous allons stocker dans la base de données (violation DRY)

Je vois assez souvent ce type de code de référentiel CRUD, il peut être utilisé, mais un couplage élevé et une duplication de code conduisent au fait qu’avec le temps, sa prise en charge deviendra très compliquée. Cela sera particulièrement visible lorsque vous tenterez de passer à une autre base de données ou lors de la modification de la logique interne de travail avec la base de données dans tous les référentiels créés dans l’application.
Au lieu de dupliquer le code, maintenez un couplage élevé : Écrivons un protocole pour le référentiel CRUD, faisant ainsi abstraction de l’interface de base de données et de la logique métier de l’application, respectant DRY, implémentant un faible couplage :

    typealias Item = Codable
    typealias ItemIdentifier = String
    
    func create<T: CRUDRepository.Item>(id: CRUDRepository.ItemIdentifier, item: T) async throws
    func read<T: CRUDRepository.Item>(id: CRUDRepository.ItemIdentifier) async throws -> T
    func update<T: CRUDRepository.Item>(id: CRUDRepository.ItemIdentifier, item: T) async throws
    func delete(id: CRUDRepository.ItemIdentifier) async throws
}

Le protocole CRUDRepository décrit les interfaces et les types de données associés pour une mise en œuvre ultérieure d’un référentiel CRUD spécifique.

Ensuite, nous écrirons une implémentation spécifique pour la base de données UserDefaults :

    private typealias RecordIdentifier = String
    
    let tableName: String
    let dataTransformer: DataTransformer
    
    init(
   	 tableName: String = "",
   	 dataTransformer: DataTransformer = JSONDataTransformer()
    ) {
   	 self.tableName = tableName
   	 self.dataTransformer = dataTransformer
    }
    
    private func key(id: CRUDRepository.ItemIdentifier) -> RecordIdentifier {
   	 "database_\(tableName)_item_\(id)"
    }
   	 
    private func isExists(id: CRUDRepository.ItemIdentifier) async throws -> Bool {
   	 UserDefaults.standard.data(forKey: key(id: id)) != nil
    }
    
    func create<T: CRUDRepository.Item>(id: CRUDRepository.ItemIdentifier, item: T) async throws {
   	 let data = try await dataTransformer.encode(item)
   	 UserDefaults.standard.set(data, forKey: key(id: id))
   	 UserDefaults.standard.synchronize()
    }
    
    func read<T: CRUDRepository.Item>(id: CRUDRepository.ItemIdentifier) async throws -> T {
   	 guard let data = UserDefaults.standard.data(forKey: key(id: id)) else {
   		 throw CRUDRepositoryError.recordNotFound(id: id)
   	 }
   	 let item: T = try await dataTransformer.decode(data: data)
   	 return item
    }
    
    func update<T: CRUDRepository.Item>(id: CRUDRepository.ItemIdentifier, item: T) async throws {
   	 guard try await isExists(id: id) else {
   		 throw CRUDRepositoryError.recordNotFound(id: id)
   	 }
   	 let data = try await dataTransformer.encode(item)
   	 UserDefaults.standard.set(data, forKey: key(id: id))
   	 UserDefaults.standard.synchronize()
    }
    
    func delete(id: CRUDRepository.ItemIdentifier) async throws {
   	 guard try await isExists(id: id) else {
   		 throw CRUDRepositoryError.recordNotFound(id: id)
   	 }
   	 UserDefaults.standard.removeObject(forKey: key(id: id))
   	 UserDefaults.standard.synchronize()
    }
}

Le code semble long, mais contient une implémentation concrète complète d’un référentiel CRUD contenant un couplage lâche, détails ci-dessous.
des typealias ont été ajoutés pour l’auto-documentation du code.
Couplage faible et couplage fort
Le détachement d’une structure spécifique (struct) est implémenté à l’aide du T générique, qui à son tour doit implémenter les protocoles Codable. Codable vous permet de convertir des structures à l’aide de classes qui implémentent les protocoles TopLevelEncoder et TopLevelDecoder, par exemple JSONEncoder et JSONDecoder, lors de l’utilisation de types de base (Int, String, Float, etc.), il n’est pas nécessaire d’écrire du code supplémentaire pour convertir les structures.

Le découplage d’un encodeur et d’un décodeur spécifiques s’effectue à l’aide de l’abstraction dans le protocole DataTransformer :

	func encode<T: Encodable>(_ object: T) async throws -> Data
	func decode<T: Decodable>(data: Data) async throws -> T
}

En utilisant l’implémentation d’un transformateur de données, nous avons implémenté une abstraction des interfaces de l’encodeur et du décodeur, en implémentant un couplage lâche pour garantir le travail avec différents types de formats de données.

Ce qui suit est le code d’un DataTransformer spécifique, à savoir pour JSON :

	func encode<T>(_ object: T) async throws -> Data where T : Encodable {
    		let data = try JSONEncoder().encode(object)
    		return data
	}
    
	func decode<T>(data: Data) async throws -> T where T : Decodable {
    		let item: T = try JSONDecoder().decode(T.self, from: data)
    		return item
	}
}

Était-ce possible ?

Qu’est-ce qui a changé ? Il suffit désormais d’initialiser un référentiel spécifique pour fonctionner avec n’importe quelle structure implémentant le protocole Codable, éliminant ainsi le besoin de dupliquer le code et de mettre en œuvre un couplage lâche de l’application.

Un exemple de client CRUD avec un référentiel spécifique, UserDefaults est la base de données, le format de données JSON, la structure client, également un exemple d’écriture et de lecture d’un tableau :


print("One item access example")

do {
	let clientRecordIdentifier = "client"
	let clientOne = Client(name: "Chill Client")
	let repository = UserDefaultsRepository(
    	tableName: "Clients Database",
    	dataTransformer: JSONDataTransformer()
	)
	try await repository.create(id: clientRecordIdentifier, item: clientOne)
	var clientRecord: Client = try await repository.read(id: clientRecordIdentifier)
	print("Client Name: \(clientRecord.name)")
	clientRecord.name = "Busy Client"
	try await repository.update(id: clientRecordIdentifier, item: clientRecord)
	let updatedClient: Client = try await repository.read(id: clientRecordIdentifier)
	print("Updated Client Name: \(updatedClient.name)")
	try await repository.delete(id: clientRecordIdentifier)
	let removedClientRecord: Client = try await repository.read(id: clientRecordIdentifier)
	print(removedClientRecord)
}
catch {
	print(error.localizedDescription)
}

print("Array access example")

let clientArrayRecordIdentifier = "clientArray"
let clientOne = Client(name: "Chill Client")
let repository = UserDefaultsRepository(
	tableName: "Clients Database",
	dataTransformer: JSONDataTransformer()
)
let array = [clientOne]
try await repository.create(id: clientArrayRecordIdentifier, item: array)
let savedArray: [Client] = try await repository.read(id: clientArrayRecordIdentifier)
print(savedArray.first!)

Lors de la première vérification CRUD, une gestion des exceptions a été implémentée, dans laquelle la lecture de l’élément distant ne sera plus disponible.

Changer de base de données

Je vais maintenant vous montrer comment transférer votre code actuel vers une autre base de données. Par exemple, je prendrai le code du référentiel SQLite généré par ChatGPT :


class SQLiteRepository: CRUDRepository {
    private typealias RecordIdentifier = String
    
    let tableName: String
    let dataTransformer: DataTransformer
    private var db: OpaquePointer?

    init(
   	 tableName: String,
   	 dataTransformer: DataTransformer = JSONDataTransformer()
    ) {
   	 self.tableName = tableName
   	 self.dataTransformer = dataTransformer
   	 self.db = openDatabase()
   	 createTableIfNeeded()
    }
    
    private func openDatabase() -> OpaquePointer? {
   	 var db: OpaquePointer? = nil
   	 let fileURL = try! FileManager.default
   		 .url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: false)
   		 .appendingPathComponent("\(tableName).sqlite")
   	 if sqlite3_open(fileURL.path, &db) != SQLITE_OK {
   		 print("error opening database")
   		 return nil
   	 }
   	 return db
    }
    
    private func createTableIfNeeded() {
   	 let createTableString = """
   	 CREATE TABLE IF NOT EXISTS \(tableName) (
   	 id TEXT PRIMARY KEY NOT NULL,
   	 data BLOB NOT NULL
   	 );
   	 """
   	 var createTableStatement: OpaquePointer? = nil
   	 if sqlite3_prepare_v2(db, createTableString, -1, &createTableStatement, nil) == SQLITE_OK {
   		 if sqlite3_step(createTableStatement) == SQLITE_DONE {
       		 print("\(tableName) table created.")
   		 } else {
       		 print("\(tableName) table could not be created.")
   		 }
   	 } else {
   		 print("CREATE TABLE statement could not be prepared.")
   	 }
   	 sqlite3_finalize(createTableStatement)
    }
    
    private func isExists(id: CRUDRepository.ItemIdentifier) async throws -> Bool {
   	 let queryStatementString = "SELECT data FROM \(tableName) WHERE id = ?;"
   	 var queryStatement: OpaquePointer? = nil
   	 if sqlite3_prepare_v2(db, queryStatementString, -1, &queryStatement, nil) == SQLITE_OK {
   		 sqlite3_bind_text(queryStatement, 1, id, -1, nil)
   		 if sqlite3_step(queryStatement) == SQLITE_ROW {
       		 sqlite3_finalize(queryStatement)
       		 return true
   		 } else {
       		 sqlite3_finalize(queryStatement)
       		 return false
   		 }
   	 } else {
   		 print("SELECT statement could not be prepared.")
   		 throw CRUDRepositoryError.databaseError
   	 }
    }
    
    func create<T: CRUDRepository.Item>(id: CRUDRepository.ItemIdentifier, item: T) async throws {
   	 let insertStatementString = "INSERT INTO \(tableName) (id, data) VALUES (?, ?);"
   	 var insertStatement: OpaquePointer? = nil
   	 if sqlite3_prepare_v2(db, insertStatementString, -1, &insertStatement, nil) == SQLITE_OK {
   		 let data = try await dataTransformer.encode(item)
   		 sqlite3_bind_text(insertStatement, 1, id, -1, nil)
   		 sqlite3_bind_blob(insertStatement, 2, (data as NSData).bytes, Int32(data.count), nil)
   		 if sqlite3_step(insertStatement) == SQLITE_DONE {
       		 print("Successfully inserted row.")
   		 } else {
       		 print("Could not insert row.")
       		 throw CRUDRepositoryError.databaseError
   		 }
   	 } else {
   		 print("INSERT statement could not be prepared.")
   		 throw CRUDRepositoryError.databaseError
   	 }
   	 sqlite3_finalize(insertStatement)
    }
    
    func read<T: CRUDRepository.Item>(id: CRUDRepository.ItemIdentifier) async throws -> T {
   	 let queryStatementString = "SELECT data FROM \(tableName) WHERE id = ?;"
   	 var queryStatement: OpaquePointer? = nil
   	 var item: T?
   	 if sqlite3_prepare_v2(db, queryStatementString, -1, &queryStatement, nil) == SQLITE_OK {
   		 sqlite3_bind_text(queryStatement, 1, id, -1, nil)
   		 if sqlite3_step(queryStatement) == SQLITE_ROW {
       		 let queryResultCol1 = sqlite3_column_blob(queryStatement, 0)
       		 let queryResultCol1Length = sqlite3_column_bytes(queryStatement, 0)
       		 let data = Data(bytes: queryResultCol1, count: Int(queryResultCol1Length))
       		 item = try await dataTransformer.decode(data: data)
   		 } else {
       		 throw CRUDRepositoryError.recordNotFound(id: id)
   		 }
   	 } else {
   		 print("SELECT statement could not be prepared")
   		 throw CRUDRepositoryError.databaseError
   	 }
   	 sqlite3_finalize(queryStatement)
   	 return item!
    }
    
    func update<T: CRUDRepository.Item>(id: CRUDRepository.ItemIdentifier, item: T) async throws {
   	 guard try await isExists(id: id) else {
   		 throw CRUDRepositoryError.recordNotFound(id: id)
   	 }
   	 let updateStatementString = "UPDATE \(tableName) SET data = ? WHERE id = ?;"
   	 var updateStatement: OpaquePointer? = nil
   	 if sqlite3_prepare_v2(db, updateStatementString, -1, &updateStatement, nil) == SQLITE_OK {
   		 let data = try await dataTransformer.encode(item)
   		 sqlite3_bind_blob(updateStatement, 1, (data as NSData).bytes, Int32(data.count), nil)
   		 sqlite3_bind_text(updateStatement, 2, id, -1, nil)
   		 if sqlite3_step(updateStatement) == SQLITE_DONE {
       		 print("Successfully updated row.")
   		 } else {
       		 print("Could not update row.")
       		 throw CRUDRepositoryError.databaseError
   		 }
   	 } else {
   		 print("UPDATE statement could not be prepared.")
   		 throw CRUDRepositoryError.databaseError
   	 }
   	 sqlite3_finalize(updateStatement)
    }
    
    func delete(id: CRUDRepository.ItemIdentifier) async throws {
   	 guard try await isExists(id: id) else {
   		 throw CRUDRepositoryError.recordNotFound(id: id)
   	 }
   	 let deleteStatementString = "DELETE FROM \(tableName) WHERE id = ?;"
   	 var deleteStatement: OpaquePointer? = nil
   	 if sqlite3_prepare_v2(db, deleteStatementString, -1, &deleteStatement, nil) == SQLITE_OK {
   		 sqlite3_bind_text(deleteStatement, 1, id, -1, nil)
   		 if sqlite3_step(deleteStatement) == SQLITE_DONE {
       		 print("Successfully deleted row.")
   		 } else {
       		 print("Could not delete row.")
       		 throw CRUDRepositoryError.databaseError
   		 }
   	 } else {
   		 print("DELETE statement could not be prepared.")
   		 throw CRUDRepositoryError.databaseError
   	 }
   	 sqlite3_finalize(deleteStatement)
    }
}

Ou le code CRUD du référentiel du système de fichiers, qui a également été généré par ChatGPT :


class FileSystemRepository: CRUDRepository {
	private typealias RecordIdentifier = String
    
	let directoryName: String
	let dataTransformer: DataTransformer
	private let fileManager = FileManager.default
	private var directoryURL: URL
    
	init(
    	directoryName: String = "Database",
    	dataTransformer: DataTransformer = JSONDataTransformer()
	) {
    	self.directoryName = directoryName
    	self.dataTransformer = dataTransformer
   	 
    	let paths = fileManager.urls(for: .documentDirectory, in: .userDomainMask)
    	directoryURL = paths.first!.appendingPathComponent(directoryName)
   	 
    	if !fileManager.fileExists(atPath: directoryURL.path) {
        	try? fileManager.createDirectory(at: directoryURL, withIntermediateDirectories: true, attributes: nil)
    	}
	}
    
	private func fileURL(id: CRUDRepository.ItemIdentifier) -> URL {
    	return directoryURL.appendingPathComponent("item_\(id).json")
	}
    
	private func isExists(id: CRUDRepository.ItemIdentifier) async throws -> Bool {
    	return fileManager.fileExists(atPath: fileURL(id: id).path)
	}
    
	func create<T: CRUDRepository.Item>(id: CRUDRepository.ItemIdentifier, item: T) async throws {
    	let data = try await dataTransformer.encode(item)
    	let url = fileURL(id: id)
    	try data.write(to: url)
	}
    
	func read<T: CRUDRepository.Item>(id: CRUDRepository.ItemIdentifier) async throws -> T {
    	let url = fileURL(id: id)
    	guard let data = fileManager.contents(atPath: url.path) else {
        	throw CRUDRepositoryError.recordNotFound(id: id)
    	}
    	let item: T = try await dataTransformer.decode(data: data)
    	return item
	}
    
	func update<T: CRUDRepository.Item>(id: CRUDRepository.ItemIdentifier, item: T) async throws {
    	guard try await isExists(id: id) else {
        	throw CRUDRepositoryError.recordNotFound(id: id)
    	}
    	let data = try await dataTransformer.encode(item)
    	let url = fileURL(id: id)
    	try data.write(to: url)
	}
    
	func delete(id: CRUDRepository.ItemIdentifier) async throws {
    	guard try await isExists(id: id) else {
        	throw CRUDRepositoryError.recordNotFound(id: id)
    	}
    	let url = fileURL(id: id)
    	try fileManager.removeItem(at: url)
	}
}

Remplacer le dépôt dans le code client :


print("One item access example")

do {
	let clientRecordIdentifier = "client"
	let clientOne = Client(name: "Chill Client")
	let repository = FileSystemRepository(
    	directoryName: "Clients Database",
    	dataTransformer: JSONDataTransformer()
	)
	try await repository.create(id: clientRecordIdentifier, item: clientOne)
	var clientRecord: Client = try await repository.read(id: clientRecordIdentifier)
	print("Client Name: \(clientRecord.name)")
	clientRecord.name = "Busy Client"
	try await repository.update(id: clientRecordIdentifier, item: clientRecord)
	let updatedClient: Client = try await repository.read(id: clientRecordIdentifier)
	print("Updated Client Name: \(updatedClient.name)")
	try await repository.delete(id: clientRecordIdentifier)
	let removedClientRecord: Client = try await repository.read(id: clientRecordIdentifier)
	print(removedClientRecord)
}
catch {
	print(error.localizedDescription)
}

print("Array access example")

let clientArrayRecordIdentifier = "clientArray"
let clientOne = Client(name: "Chill Client")
let repository = FileSystemRepository(
	directoryName: "Clients Database",
	dataTransformer: JSONDataTransformer()
)
let array = [clientOne]
try await repository.create(id: clientArrayRecordIdentifier, item: array)
let savedArray: [Client] = try await repository.read(id: clientArrayRecordIdentifier)
print(savedArray.first!)

L’initialisation de UserDefaultsRepository a été remplacée par FileSystemRepository, avec les arguments appropriés.
Après avoir exécuté la deuxième version du code client, vous trouverez un répertoire « Clients Database » dans le dossier documents, qui contiendra un fichier d’un tableau sérialisé en JSON avec une structure Client.

Changement de format de stockage des données

Demandons maintenant à ChatGPT de générer un encodeur et un décodeur pour XML :

	let formatExtension = "xml"
    
	func encode<T: Encodable>(_ item: T) async throws -> Data {
    	let encoder = PropertyListEncoder()
    	encoder.outputFormat = .xml
    	return try encoder.encode(item)
	}
    
	func decode<T: Decodable>(data: Data) async throws -> T {
    	let decoder = PropertyListDecoder()
    	return try decoder.decode(T.self, from: data)
	}
}

Grâce aux types intégrés dans Swift, la tâche d’un réseau de neurones devient élémentaire.

Remplacez JSON par XML dans le code client :


print("One item access example")

do {
	let clientRecordIdentifier = "client"
	let clientOne = Client(name: "Chill Client")
	let repository = FileSystemRepository(
    	directoryName: "Clients Database",
    	dataTransformer: XMLDataTransformer()
	)
	try await repository.create(id: clientRecordIdentifier, item: clientOne)
	var clientRecord: Client = try await repository.read(id: clientRecordIdentifier)
	print("Client Name: \(clientRecord.name)")
	clientRecord.name = "Busy Client"
	try await repository.update(id: clientRecordIdentifier, item: clientRecord)
	let updatedClient: Client = try await repository.read(id: clientRecordIdentifier)
	print("Updated Client Name: \(updatedClient.name)")
	try await repository.delete(id: clientRecordIdentifier)
	let removedClientRecord: Client = try await repository.read(id: clientRecordIdentifier)
	print(removedClientRecord)
}
catch {
	print(error.localizedDescription)
}

print("Array access example")

let clientArrayRecordIdentifier = "clientArray"
let clientOne = Client(name: "Chill Client")
let repository = FileSystemRepository(
	directoryName: "Clients Database",
	dataTransformer: XMLDataTransformer()
)
let array = [clientOne]
try await repository.create(id: clientArrayRecordIdentifier, item: array)
let savedArray: [Client] = try await repository.read(id: clientArrayRecordIdentifier)
print(savedArray.first!)

Le code client a été remplacé par une seule expression JSONDataTransformer -> XMLDataTransformer

Total

Les référentiels CRUD sont l’un des modèles de conception qui peuvent être utilisés pour implémenter un couplage lâche des composants de l’architecture d’application. Une autre des solutions – en utilisant ORM (Object-Relational Mapping), en bref, ORM utilise une approche dans laquelle les structures sont entièrement mappées à la base de données, puis les modifications avec les modèles doivent être affichées (mappées (!)) sur la base de données.
Mais c’est une toute autre histoire.

Une implémentation complète des référentiels CRUD pour Swift est disponible sur :
https://gitlab.com/demensdeum/crud-example

À propos, Swift est pris en charge en dehors de macOS depuis longtemps ; le code de l’article a été entièrement écrit et testé sur Arch Linux.

Sources

https://developer.apple.com/documentation/combine/topleveldecoder
https://developer.apple.com/documentation/combine/toplevelencoder
https://en.wikipedia.org/wiki/Create,_read,_update_and_delete

jj erreur d’entrée/sortie

Que devez-vous faire si vous recevez une erreur d’entrée/sortie lors de la copie d’un disque normal à l’aide de dd sous Linux ?

Situevina est très triste, mais résoluble. Il s’agit très probablement d’un disque défaillant contenant des blocs défectueux qui ne peuvent plus être utilisés, écrits ou lus.

Assurez-vous de vérifier un tel disque à l’aide de S.M.A.R.T., il est fort probable qu’il vous montrera des erreurs de disque. Ce fut le cas dans mon cas ; le nombre de blocs défectueux était si énorme que j’ai dû dire au revoir à l’ancien disque dur et le remplacer par un nouveau SSD.

Le problème était que ce disque contenait un système entièrement fonctionnel avec un logiciel sous licence nécessaire au travail. J’ai essayé d’utiliser partimage pour copier rapidement des données, mais tout à coup j’ai découvert que l’utilitaire ne copie qu’un tiers du disque, puis cela se termine soit par une erreur de segmentation, soit par une autre blague amusante de Sishny/Sipplusplus.

Ensuite, j’ai essayé de copier les données en utilisant dd, et il s’est avéré que dd arrive à peu près au même endroit que partimage, puis une erreur d’entrée/sortie se produit. En même temps, toutes sortes de drapeaux amusants comme conv=noerr, skip ou autre n’aidaient pas du tout.

Mais les données ont été copiées sur un autre disque sans problème à l’aide d’un utilitaire GNU appelé ddrescue.

После этого мои волосы стали шелковистыми, вернулась жена, дети и собака перестала кусать диван.

Большим плюсом ddrescue является наличие встроенного прогрессбара, поэтому не приходится костылять какие-то ухищрения навроде pv и всяких не особо красивых флажков dd. Также ddrescure показывает количество попыток прочитать данные; еще на вики написано что утилита обладает каким-то сверх алгоритмом для считывания поврежденных данных, оставим это на проверку людям которые любят ковыряться в исходниках, мы же не из этих да?

https://ru.wikipedia.org/wiki/Ddrescue
https://www.gnu.org/software/ddrescue/ddrescue_ru.html

ChatGPT

Bonjour à tous ! Dans cet article, je veux parler de ChatGPT – une modélisation de langage puissante d’OpenAI qui peut aider à résoudre une variété de problèmes de traitement de texte. Je vais vous montrer comment fonctionne cet outil et comment il peut être utilisé dans des situations pratiques. Commençons !

À l’heure actuelle, ChatGPT est l’un des meilleurs modèles linguistiques au monde basés sur les réseaux de neurones. Il a été créé dans le but d’aider les développeurs à créer des systèmes intelligents capables de générer un langage naturel et de communiquer avec les personnes qui l’utilisent.

L’un des principaux avantages de ChatGPT est sa capacité à modéliser le texte de manière contextuelle. Cela signifie que le modèle prend en compte le dialogue précédent et l’utilise pour comprendre plus précisément la situation et générer une réponse plus naturelle.

Vous pouvez utiliser ChatGPT pour diverses tâches, telles que l’automatisation du support client, la création de chatbots, la génération de texte et bien plus encore.

Les réseaux neuronaux derrière ChatGPT ont été entraînés sur d’énormes quantités de texte pour garantir des prédictions très précises. Cela permet au modèle de générer un texte naturel pouvant prendre en charge le dialogue et répondre aux questions.

Avec ChatGPT, vous pouvez créer vos propres chatbots et autres systèmes intelligents capables d’interagir avec les gens en langage naturel. Cela peut être particulièrement utile dans des secteurs tels que le tourisme, la vente au détail et le service client.

En conclusion, ChatGPT – c’est un outil puissant pour résoudre divers problèmes de modélisation du langage. Sa capacité de modélisation contextuelle le rend particulièrement utile pour créer des chatbots et des systèmes intelligents.

En fait, ChatGPT a entièrement écrit tout ce qui précède. Quoi? Oui? Je suis moi-même choqué !

Vous pouvez essayer la grille elle-même ici :
https://chat.openai.com/chat

Activer le rétroéclairage du clavier USB sur macOS

J’ai récemment acheté un clavier USB Getorix GK-45X très bon marché avec rétroéclairage RVB. Après l’avoir connecté à un MacBook Pro équipé d’un processeur M1, il est devenu évident que le rétroéclairage RVB ne fonctionnait pas. Même en appuyant sur la combinaison magique Fn + Scroll Lock, seul le niveau de rétroéclairage de l’écran du MacBook a changé.
Il existe plusieurs solutions à ce problème, à savoir OpenRGB (ne fonctionne pas), HID LED Test (ne fonctionne pas). Seul l’utilitaire kvmswitch a fonctionné :
https://github.com/stoutput/OSX-KVM

Vous devez le télécharger depuis GitHub et l’autoriser à s’exécuter à partir du terminal dans le panneau de sécurité des paramètres système.
Si je comprends bien de la description, après avoir lancé l’utilitaire, il envoie une pression Fn + Scroll Lock, allumant/éteignant ainsi le rétroéclairage du clavier.

Mitsume ga Tōru (NES) – Troisième œil sur Dandy

https://www.youtube.com/watch?v=LT2U3CJnzxU

Mitsume ga Tooru (三つ目がとおる Mitsume ga tōru ?, lit. « à trois yeux ») est un jeu vidéo de plateforme développé par Natsume en 1992 exclusivement pour la Nintendo Entertainment System. Le jeu est basé sur le manga et l’anime Mitsume ga Tooru. Il s’agit du deuxième jeu basé sur un anime développé par Natsume ; le précédent était Mitsume ga Tooru : 3Lie-Mon, sorti sur la plateforme MSX deux ans plus tôt. En Russie, le jeu est mieux connu sous le nom de « 3 Eyes », ou « 3 Eyes ». Troisième œil….

Number 2

Comrades, I take pride in projects that were created on the basis of Flame Steel Framework 1 and specifically on Flame Steel Engine 1, namely Death-Mask, Cube Art Project, since all this was conceived as a big experiment, creating a multimedia framework alone that can work on the most platforms. I think the experiment ended successfully immediately after the release of the Cube Art Project.

Now about the decisions that I came to during the development of new projects on FSFramework 1

During the development of Space Jaguar and the Space Jaguar Galaxy Bastards shooter, it became clear that the Flame Steel Framework tools were already outdated, not even having time to become at least somewhat convenient.

Therefore, I decided to develop a completely new Flame Steel Framework 2. The main decision will be to switch to my Rise 2 transpiler language, and the Component System (ECS) will no longer be used architecturally, because. it turned out to be needed only within the framework of game logic with great dynamics. For this reason, in Flame Steel Framework 2, the component system will only be possible while using the scripting languages ​​that are planned to be implemented (at least Lua and JavaScript), an interesting feature is that these languages ​​​​are dynamic in nature, so additional creation of the component system is redundant.

You can follow the development of new projects on the blog and on Gitlab:

https://gitlab.com/demensdeum/rise2

https://gitlab.com/demensdeum/flamesteelengine2

https://gitlab.com/demensdeum/flame-steel-engine-2-demo-projects

https://gitlab.com/demensdeum/space-jaguar-action-rpg

https://gitlab.com/demensdeum/space-jaguar-galaxy-bastards

Tri des arbres

Tri par arbre : tri à l’aide d’un arbre de recherche binaire. Complexité temporelle – O(n²). Dans un tel arbre, chaque nœud à gauche a des nombres inférieurs au nœud, à droite il y en a plus que le nœud, en venant de la racine et en imprimant les valeurs de gauche à droite, on obtient une liste triée de nombres . Surprenant, non ?

Considérez l’arbre de recherche binaire :

Derrick Coetzee (domaine public)

Essayez de lire manuellement les nombres en partant de l’avant-dernier nœud gauche du coin inférieur gauche, pour chaque nœud à gauche – un nœud – à droite.

Cela ressemblera à ceci :

  1. Avant-dernier nœud en bas à gauche – 3.
  2. Il a une branche gauche – 1.
  3. Prenez ce numéro (1)
  4. Ensuite, nous prenons le sommet 3 (1, 3)
  5. À droite se trouve la branche 6, mais elle contient des branches. Par conséquent, nous le lisons de la même manière.
  6. Branche gauche du nœud 6 numéro 4 (1, 3, 4)
  7. Le nœud lui-même est 6 (1, 3, 4, 6)
  8. Droite 7 (1, 3, 4, 6, 7)
  9. Montez jusqu’au nœud racine – 8 (1,3, 4,6, 7, 8)
  10. Nous imprimons tout à droite par analogie
  11. Nous obtenons la liste finale – 1, 3, 4, 6, 7, 8, 10, 13, 14

Pour implémenter l’algorithme dans le code, vous aurez besoin de deux fonctions :

  1. Assembler un arbre de recherche binaire
  2. Imprimer l’arbre de recherche binaire dans le bon ordre

L’arbre binaire de recherche est assemblé de la même manière qu’il est lu, un numéro est attaché à chaque nœud à gauche ou à droite, selon qu’il est inférieur ou supérieur.

Exemple en Lua :


function Node:new(value, lhs, rhs)
    output = {}
    setmetatable(output, self)
    self.__index = self  
    output.value = value
    output.lhs = lhs
    output.rhs = rhs
    output.counter = 1
    return output  
end

function Node:Increment()
    self.counter = self.counter + 1
end

function Node:Insert(value)
    if self.lhs ~= nil and self.lhs.value > value then
        self.lhs:Insert(value)
        return
    end

    if self.rhs ~= nil and self.rhs.value < value then
        self.rhs:Insert(value)
        return
    end

    if self.value == value then
        self:Increment()
        return
    elseif self.value > value then
        if self.lhs == nil then
            self.lhs = Node:new(value, nil, nil)
        else
            self.lhs:Insert(value)
        end
        return
    else
        if self.rhs == nil then
            self.rhs = Node:new(value, nil, nil)
        else
            self.rhs:Insert(value)
        end
        return
    end
end

function Node:InOrder(output)
    if self.lhs ~= nil then
       output = self.lhs:InOrder(output)
    end
    output = self:printSelf(output)
    if self.rhs ~= nil then
        output = self.rhs:InOrder(output)
    end
    return output
end

function Node:printSelf(output)
    for i=0,self.counter-1 do
        output = output .. tostring(self.value) .. " "
    end
    return output
end

function PrintArray(numbers)
    output = ""
    for i=0,#numbers do
        output = output .. tostring(numbers[i]) .. " "
    end    
    print(output)
end

function Treesort(numbers)
    rootNode = Node:new(numbers[0], nil, nil)
    for i=1,#numbers do
        rootNode:Insert(numbers[i])
    end
    print(rootNode:InOrder(""))
end


numbersCount = 10
maxNumber = 9

numbers = {}

for i=0,numbersCount-1 do
    numbers[i] = math.random(0, maxNumber)
end

PrintArray(numbers)
Treesort(numbers)

Важный нюанс что для чисел которые равны вершине придумано множество интересных механизмов подцепления к ноде, я же просто добавил счетчик к классу вершины, при распечатке числа возвращаются по счетчику.

Ссылки

https://gitlab.com/demensdeum/algorithms/-/tree/master/sortAlgorithms/treesort

Источники

TreeSort Algorithm Explained and Implemented with Examples in Java | Sorting Algorithms | Geekific – YouTube

Tree sort – YouTube

Convert Sorted Array to Binary Search Tree (LeetCode 108. Algorithm Explained) – YouTube

Sorting algorithms/Tree sort on a linked list – Rosetta Code

Tree Sort – GeeksforGeeks

Tree sort – Wikipedia

How to handle duplicates in Binary Search Tree? – GeeksforGeeks

Tree Sort | GeeksforGeeks – YouTube

Tri par seau

Tri par seaux : tri par seaux. L’algorithme est similaire au tri par comptage, à la différence que les nombres sont collectés dans des plages de « seaux », puis les seaux sont triés à l’aide de tout autre algorithme de tri suffisamment productif, et l’étape finale consiste à déplier les « seaux » par un, ce qui donne une liste triée

La complexité temporelle de l’algorithme est O(nk). L’algorithme fonctionne en temps linéaire pour des données obéissant à une loi de distribution uniforme. Pour faire simple, les éléments doivent être dans une certaine plage, sans « pointes », par exemple des nombres de 0,0 à 1,0. Si parmi ces nombres il y en a 4 ou 999, alors selon les lois de la cour, une telle rangée n’est plus considérée comme « paire ».

Exemple d’implémentation dans Julia :

    buckets = Vector{Vector{Int}}()
    
    for i in 0:bucketsCount - 1
        bucket = Vector{Int}()
        push!(buckets, bucket)
    end

    maxNumber = maximum(numbers)

    for i in 0:length(numbers) - 1
        bucketIndex = 1 + Int(floor(bucketsCount * numbers[1 + i] / (maxNumber + 1)))
        push!(buckets[bucketIndex], numbers[1 + i])
    end

    for i in 0:length(buckets) - 1
        bucketIndex = 1 + i
        buckets[bucketIndex] = sort(buckets[bucketIndex])
    end

    flat = [(buckets...)...]
    print(flat, "\n")

end

numbersCount = 10
maxNumber = 10
numbers = rand(1:maxNumber, numbersCount)
print(numbers,"\n")
bucketsCount = 10
bucketSort(numbers, bucketsCount)

На производительность алгоритма также влияет число ведер, для большего количества чисел лучше взять большее число ведер (Algorithms in a nutshell by George T. Heineman)

Ссылки

https://gitlab.com/demensdeum/algorithms/-/tree/master/sortAlgorithms/bucketSort

Источники

https://www.youtube.com/watch?v=VuXbEb5ywrU
https://www.youtube.com/watch?v=ELrhrrCjDOA
https://medium.com/karuna-sehgal/an-introduction-to-bucket-sort-62aa5325d124
https://www.geeksforgeeks.org/bucket-sort-2/
https://ru.wikipedia.org/wiki/%D0%91%D0%BB%D0%BE%D1%87%D0%BD%D0%B0%D1%8F_%D1%81%D0%BE%D1%80%D1%82%D0%B8%D1%80%D0%BE%D0%B2%D0%BA%D0%B0
https://www.youtube.com/watch?v=LPrF9yEKTks
https://en.wikipedia.org/wiki/Bucket_sort
https://julialang.org/
https://www.oreilly.com/library/view/algorithms-in-a/9780596516246/ch04s08.html

Tri par base

Tri Radix – tri par base. L’algorithme est similaire au tri par comptage dans la mesure où il n’y a pas de comparaison des éléments ; à la place, les éléments sont regroupés *caractère par caractère* dans des *buckets* (buckets), le bucket est sélectionné par l’index du numéro-caractère actuel. Complexité temporelle – O(nd).

Cela fonctionne comme ceci :

  • L’entrée sera les nombres 6, 12, 44, 9
  • Nous allons créer 10 groupes de listes (0 à 9), dans lesquels nous ajouterons/trierons des nombres petit à petit.

Suivant :

  1. Démarrer une boucle avec le compteur i jusqu’au nombre maximum de caractères du nombre
  2. Par l’index i de droite à gauche, nous obtenons un symbole pour chaque nombre ; s’il n’y a pas de symbole, alors nous supposons qu’il est nul
  3. Convertir le symbole en nombre
  4. Sélectionnez un bucket par numéro d’index et insérez-y le numéro entier
  5. Après avoir terminé la recherche parmi les nombres, reconvertissez tous les compartiments en une liste de nombres
  6. Obtenir des chiffres triés par rang
  7. Répétez jusqu’à ce que tous les chiffres aient disparu

Exemple de tri Radix dans Scala :


import scala.util.Random.nextInt



object RadixSort {

    def main(args: Array[String]) = {

        var maxNumber = 200

        var numbersCount = 30

        var maxLength = maxNumber.toString.length() - 1



        var referenceNumbers = LazyList.continually(nextInt(maxNumber + 1)).take(numbersCount).toList

        var numbers = referenceNumbers

        

        var buckets = List.fill(10)(ListBuffer[Int]())



        for( i <- 0 to maxLength) { numbers.foreach( number => {

                    var numberString = number.toString

                    if (numberString.length() > i) {

                        var index = numberString.length() - i - 1

                        var character = numberString.charAt(index).toString

                        var characterInteger = character.toInt  

                        buckets.apply(characterInteger) += number

                    }

                    else {

                        buckets.apply(0) += number

                    }

                }

            )

            numbers = buckets.flatten

            buckets.foreach(x => x.clear())

        }

        println(referenceNumbers)

        println(numbers)

        println(s"Validation result: ${numbers == referenceNumbers.sorted}")

    }

}

L’algorithme dispose également d’une version pour une exécution parallèle, par exemple sur un GPU ; Il y a aussi une petite option de tri, qui doit êtretrès intéressante et vraiment époustouflante !

Liens

https://gitlab .com/demensdeum/algorithms/-/blob/master/sortAlgorithms/radixSort/radixSort.scala

Sources

https://ru.wikipedia.org/wiki/%D0%9F%D0%BE%D1%80%D0%B0%D0%B7%D1%80%D1%8F%D 0%B4%D0%BD%D0%B0%D1%8F_%D1%81%D0%BE%D1%80%D1%82%D0%B8%D1%80%D0%BE%D0%B2%D0% BA%D0%B0
https://www.geeksforgeeks.org/radix-sort/

https://www.youtube.com/watch?v=toAlAJKojos

https://github.com/gyatskov/radix-sort

Tri en tas

Heapsort – tri pyramidal. Complexité temporelle de l’algorithme – O(n log n), vite, non ? J’appellerais ce tri le tri des cailloux qui tombent. Il me semble que la façon la plus simple de l’expliquer est visuellement.

L’entrée est une liste de nombres, par exemple :
5, 0, 7, 2, 3, 9, 4

De gauche à droite, une structure de données est créée : un arbre binaire, ou comme je l’appelle : un arbre binaire. pyramide. Les éléments pyramidaux peuvent avoir un maximum de deux éléments enfants, mais un seul élément supérieur.

Créons un arbre binaire :
⠀⠀5
⠀0⠀7
2 3 9 4

Si vous regardez la pyramide pendant longtemps, vous pouvez voir que ce ne sont que des nombres provenant d’un tableau, se succédant les uns après les autres, le nombre d’éléments dans chaque étage est multiplié par deux.

Ensuite, le plaisir commence : trions la pyramide de bas en haut en utilisant la méthode des cailloux tombants (tassifier). Le tri pourrait être lancé depuis le dernier étage (2 3 9 4), mais cela ne sert à rien car il n’y a pas d’étage en dessous où vous pourriez tomber.

Par conséquent, nous commençons à supprimer les éléments de l’avant-dernier étage (0 7)
⠀⠀5
⠀0⠀7
2 3 9 4

Le premier élément à tomber est sélectionné à droite, dans notre cas c’est 7, puis nous regardons ce qu’il y a en dessous, et en dessous se trouvent 9 et 4, neuf est supérieur à quatre, et neuf est également supérieur à Sept! Nous déposons 7 sur 9 et soulevons 9 à la place 7.
⠀⠀5
⠀0⠀9
2 3 7 4

Ensuite, on comprend que sept n’a nulle part où descendre plus bas, on passe au chiffre 0, qui se situe à l’avant-dernier étage à gauche :
⠀⠀5
0⠀9
2 3 7 4

Voyons ce qu’il y a en dessous – 2 et 3, deux est inférieur à trois, trois est supérieur à zéro, donc on échange zéro et trois :
⠀⠀5
⠀3⠀9
2 0 7 4

Lorsque vous atteignez le bout de l’étage, allez à l’étage supérieur et déposez tout là-bas si vous le pouvez.
Le résultat est une structure de données – un tas, à savoir max tas, car en haut se trouve le plus grand élément :
⠀⠀9
⠀3⠀7
2 0 5 4

Si vous le renvoyez à une représentation matricielle, vous obtenez une liste :
[9, 3, 7, 2, 0, 5, 4]

De là, nous pouvons conclure qu’en échangeant le premier et le dernier élément, nous obtenons le premier nombre dans la position triée finale, à savoir que 9 doit être à la fin de la liste triée, échangez les places :
[4, 3, 7, 2, 0, 5, 9]

Regardons un arbre binaire :
⠀⠀4
⠀3⠀7
2 0 5 9

Le résultat est une situation dans laquelle la partie inférieure de l’arbre est triée, il suffit d’en déposer 4 à la bonne position, de répéter l’algorithme, mais de ne pas prendre en compte les nombres déjà triés, à savoir 9 :
⠀⠀4
⠀3⠀7
2 0 5 9

⠀⠀7
⠀3⠀4
2 0 5 9

⠀⠀7
⠀3⠀5
2 0 4 9

Il s’est avéré que nous, après avoir perdu 4, avons soulevé le deuxième plus grand nombre après 9 – 7. Échangez le dernier numéro non trié (4) et le plus grand nombre (7)
⠀⠀4
⠀3⠀5
2 0 7 9

Il s’avère que nous avons maintenant deux nombres dans la bonne position finale :
4, 3, 5, 2, 0, 7, 9

Ensuite, nous répétons l’algorithme de tri, en ignorant ceux déjà triés, à la fin nous obtenons un tas :
⠀⠀0
⠀2⠀3
4 5 7 9

Ou sous forme de liste :
0, 2, 3, 4, 5, 7, 9

Mise en œuvre

L’algorithme est généralement divisé en trois fonctions :

  1. Créer un tas
  2. Algorithme de tamisage (heapify)
  3. Remplacement du dernier élément non trié et du premier

Le tas est créé en parcourant l’avant-dernière ligne de l’arbre binaire à l’aide de la fonction heapify, de droite à gauche, jusqu’à la fin du tableau. Ensuite dans le cycle, le premier remplacement des nombres est effectué, après quoi le premier élément tombe/reste en place, à la suite de quoi le plus grand élément tombe à la première place, le cycle se répète avec une diminution du nombre de participants d’un, car après chaque passage, les nombres triés restent à la fin de la liste.

Exemple de tri par tas dans Ruby :






module Colors



    BLUE = "\033[94m"



    RED = "\033[31m"



    STOP = "\033[0m"



end







def heapsort(rawNumbers)



    numbers = rawNumbers.dup







    def swap(numbers, from, to)



        temp = numbers[from]



        numbers[from] = numbers[to]



        numbers[to] = temp



    end







    def heapify(numbers)



        count = numbers.length()



        lastParentNode = (count - 2) / 2







        for start in lastParentNode.downto(0)



            siftDown(numbers, start, count - 1)



            start -= 1 



        end







        if DEMO



            puts "--- heapify ends ---"



        end



    end







    def siftDown(numbers, start, rightBound)      



        cursor = start



        printBinaryHeap(numbers, cursor, rightBound)







        def calculateLhsChildIndex(cursor)



            return cursor * 2 + 1



        end







        def calculateRhsChildIndex(cursor)



            return cursor * 2 + 2



        end            







        while calculateLhsChildIndex(cursor) <= rightBound



            lhsChildIndex = calculateLhsChildIndex(cursor)



            rhsChildIndex = calculateRhsChildIndex(cursor)







            lhsNumber = numbers[lhsChildIndex]



            biggerChildIndex = lhsChildIndex







            if rhsChildIndex <= rightBound



                rhsNumber = numbers[rhsChildIndex]



                if lhsNumber < rhsNumber



                    biggerChildIndex = rhsChildIndex



                end



            end







            if numbers[cursor] < numbers[biggerChildIndex]



                swap(numbers, cursor, biggerChildIndex)



                cursor = biggerChildIndex



            else



                break



            end



            printBinaryHeap(numbers, cursor, rightBound)



        end



        printBinaryHeap(numbers, cursor, rightBound)



    end







    def printBinaryHeap(numbers, nodeIndex = -1, rightBound = -1)



        if DEMO == false



            return



        end



        perLineWidth = (numbers.length() * 4).to_i



        linesCount = Math.log2(numbers.length()).ceil()



        xPrinterCount = 1



        cursor = 0



        spacing = 3



        for y in (0..linesCount)



            line = perLineWidth.times.map { " " }



            spacing = spacing == 3 ? 4 : 3



            printIndex = (perLineWidth / 2) - (spacing * xPrinterCount) / 2



            for x in (0..xPrinterCount - 1)



                if cursor >= numbers.length



                    break



                end



                if nodeIndex != -1 && cursor == nodeIndex



                    line[printIndex] = "%s%s%s" % [Colors::RED, numbers[cursor].to_s, Colors::STOP]



                elsif rightBound != -1 && cursor > rightBound



                    line[printIndex] = "%s%s%s" % [Colors::BLUE, numbers[cursor].to_s, Colors::STOP]



                else



                    line[printIndex] = numbers[cursor].to_s



                end



                cursor += 1



                printIndex += spacing



            end



            print line.join()



            xPrinterCount *= 2           



            print "\n"            



        end



    end







    heapify(numbers)



    rightBound = numbers.length() - 1







    while rightBound > 0



        swap(numbers, 0, rightBound)   



        rightBound -= 1



        siftDown(numbers, 0, rightBound)     



    end







    return numbers



end







numbersCount = 14



maximalNumber = 10



numbers = numbersCount.times.map { Random.rand(maximalNumber) }



print numbers



print "\n---\n"







start = Time.now



sortedNumbers = heapsort(numbers)



finish = Time.now



heapSortTime = start - finish







start = Time.now



referenceSortedNumbers = numbers.sort()



finish = Time.now



referenceSortTime = start - finish







print "Reference sort: "



print referenceSortedNumbers



print "\n"



print "Reference sort time: %f\n" % referenceSortTime



print "Heap sort:      "



print sortedNumbers



print "\n"



if DEMO == false



    print "Heap sort time:      %f\n" % heapSortTime



else



    print "Disable DEMO for performance measure\n"



end







if sortedNumbers != referenceSortedNumbers



    puts "Validation failed"



    exit 1



else



    puts "Validation success"



    exit 0



end



Cet algorithme n’est pas facile à comprendre sans visualisation, donc la première chose que je recommande est d’écrire une fonction qui imprimera la vue actuelle de l’arbre binaire.

Liens

https://gitlab.com/demensdeum/algorithms/-/blob/master/sortAlgorithms/heapsort/heapsort.rb

Sources

http://rosettacode.org/wiki/Sorting_algorithms/Heapsort
https://www.youtube.com/watch?v=LbB357_RwlY

https://habr.com/ru/company/ otus/blog/460087/

https://ru.wikipedia.org/wiki/Pyramid_sort

https://neerc.ifmo.ru/wiki /index.php?title=Heap_sort

https://wiki5.ru/wiki/Heapsort

https://wiki.c2.com/?HeapSort

https://ru.wikipedia.org/wiki/Tree (structure des données)

https://ru.wikipedia.org/wiki/Heap (structure des données)

https://www.youtube.com/watch?v=2DmK_H7IdTo

https://www.youtube.com/watch?v=kU4KBD4NFtw

https://www.youtube.com/watch?v=DU1uG5310x0

https://www.youtube.com/watch?v =BzQGPA_v-vc

https://www.geeksforgeeks.org/ représentation-tableau-de-tas-binaire/

https://habr.com/ru/post/112222/

https://www.cs.usfca. edu/~galles/visualization/BST.html

https://www.youtube.com/watch?v=EQzqHWtsKq4

https://medium.com/@dimko1/%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC% D1 %8B-%D1%81%D0%BE%D1%80%D1%82%D0%B8%D1%80%D0%BE%D0%B2%D0%BA%D0%B8-heapsort-796ba965018b

https://ru.wikibrief.org/wiki/Heapsort

https://www.youtube.com/watch?v=GUUpmrTnNbw

Bumblebee All Troubles

Recently, it turned out that users of modern Nvidia GPUs under Arch Linux do not need to use the bumblebee package at all, for example, for me it did not detect an external monitor when connected. I recommend removing the bumblebee package and all related packages, and installing prime using the instructions on the Arch Wiki.
Next, to launch all games on Steam and 3D applications, add prime-run, for Steam this is done like this prime-run %command% in additional launch options.
To check the correctness, you can use glxgears, prime-run glxgears.
https://bbs.archlinux.org/viewtopic.php? pid=2048195#p2048195

Tri rapide

Quicksort est un algorithme de tri diviser pour régner. De manière récursive, pièce par pièce, nous analysons le tableau de nombres, en plaçant les nombres dans un ordre de plus en plus grand à partir de l’élément de référence sélectionné, et insérons l’élément de référence lui-même dans la coupure entre eux. Après plusieurs itérations récursives, vous vous retrouverez avec une liste triée. Complexité temporelle O(n2).

Schéma :

  1. Nous commençons par obtenir une liste d’éléments de l’extérieur, les limites de tri. Dans un premier temps, les limites de tri s’étendront du début à la fin.
  2. Vérifiez que les limites de début et de fin ne se croisent pas ; si cela se produit, il est temps de terminer
  3. Sélectionnez un élément dans la liste et appelez-le pivot
  4. Déplacez-le vers la droite jusqu’à la fin du dernier index afin qu’il ne gêne pas
  5. Créez un compteur de *nombres plus petits* toujours égaux à zéro
  6. Parcourez la liste de gauche à droite, jusqu’au dernier index inclus où se trouve l’élément de référence
  7. Nous comparons chaque élément avec celui de référence
  8. S’il est inférieur à celui de référence, alors nous l’échangeons en fonction de l’index du compteur des nombres plus petits. Incrémentez le compteur des nombres plus petits.
  9. Lorsque la boucle atteint l’élément de support, nous nous arrêtons et échangeons l’élément de support avec l’élément en fonction du compteur de nombres plus petits.
  10. Nous exécutons l’algorithme séparément pour la plus petite partie gauche de la liste et séparément pour la plus grande partie droite de la liste.
  11. Par conséquent, toutes les itérations récursives commenceront à s’arrêter en raison de l’enregistrement au point 2
  12. Obtenir une liste triée

Quicksort a été inventé par le scientifique Charles Anthony Richard Hoare de l’Université d’État de Moscou. Ayant appris le russe, il a étudié la traduction informatique ainsi que la théorie des probabilités à l’école Kolmogorov. En 1960, en raison de la crise politique, il quitte l’Union soviétique.

Exemple d’implémentation dans Rust :


use rand::Rng;

fn swap(numbers: &mut [i64], from: usize, to: usize) {
    let temp = numbers[from];
    numbers[from] = numbers[to];
    numbers[to] = temp;
}

fn quicksort(numbers: &mut [i64], left: usize, right: usize) {
    if left >= right {
        return
    }
    let length = right - left;
    if length <= 1 {
        return
    }
    let pivot_index = left + (length / 2);
    let pivot = numbers[pivot_index];

    let last_index = right - 1;
    swap(numbers, pivot_index, last_index);

    let mut less_insert_index = left;

    for i in left..last_index {
        if numbers[i] < pivot {
            swap(numbers, i, less_insert_index);
            less_insert_index += 1;
        }
    }
    swap(numbers, last_index, less_insert_index);
    quicksort(numbers, left, less_insert_index);
    quicksort(numbers, less_insert_index + 1, right);
}

fn main() {
    let mut numbers = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
    let mut reference_numbers = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0];

    let mut rng = rand::thread_rng();
    for i in 0..numbers.len() {
        numbers[i] = rng.gen_range(-10..10);
        reference_numbers[i] = numbers[i];
    }

    reference_numbers.sort();

  println!("Numbers           {:?}", numbers);
  let length = numbers.len();
  quicksort(&mut numbers, 0, length);
  println!("Numbers           {:?}", numbers);
  println!("Reference numbers {:?}", reference_numbers);

  if numbers != reference_numbers {
    println!("Validation failed");
    std::process::exit(1);
  }
  else {
    println!("Validation success!");
    std::process::exit(0);
  }
}

Si rien n’est clair, je vous suggère de regarder la vidéo de Rob Edwards de l’Université de San Diego https://www.youtube.com/watch?v=ZHVk2blR45Q il montre très simplement, étape par étape, l’essence et la mise en œuvre de l’algorithme.

Liens

https://gitlab.com/demensdeum /algorithms/-/tree/master/sortAlgorithms/quickSort

Sources

https://www.youtube.com/watch?v =4s-aG6yGGLU
https://www.youtube.com/watch?v=ywWBy6J5gz8
https://www.youtube.com/watch?v=Hoixgm4-P4M
https://ru.wikipedia.org/wiki/Быстрая_сортировка
https://www.youtube.com/watch?v=Hoixgm4-P4M
https://www.youtube.com/watch?v=XE4VP_8Y0BU
https://www.youtube.com/watch?v=MZaf_9IZCrc
https://www.youtube.com/watch?v=ZHVk2blR45Q
http://rosettacode.org/wiki/Sorting_algorithms/Quicksort
https://www.youtube.com/watch?v=4s-aG6yGGLU
https://www.youtube.com/watch?v=dQw4w9WgXcQ
https://www.youtube.com/watch?v=maibrCbZWKw
https://www.geeksforgeeks.org/quick-sort/
https://www.youtube.com/watch?v=uXBnyYuwPe8

Tri par insertion binaire

Le tri par insertion binaire est une variante du tri par insertion dans laquelle la position d’insertion est déterminée à l’aide d’une recherche binaire. La complexité temporelle de l’algorithme est O(n2)

L’algorithme fonctionne comme ceci :

  1. Une boucle commence de zéro jusqu’à la fin de la liste
  2. Dans la boucle, un nombre est sélectionné pour le tri, le nombre est stocké dans une variable distincte
  3. La recherche binaire recherche l’index pour insérer ce numéro par rapport aux chiffres de gauche
  4. Une fois l’index trouvé, les nombres de gauche sont décalés d’une position vers la droite, en commençant à l’index d’insertion. Au cours du processus, le numéro à trier sera effacé.
  5. Le numéro précédemment enregistré est inséré à l’index d’insertion
  6. A la fin de la boucle, toute la liste sera triée

Lors d’une recherche binaire, il est possible que le numéro ne soit pas trouvé et que l’index ne soit pas renvoyé. En raison de la particularité de la recherche binaire, le nombre le plus proche de celui recherché sera trouvé, puis pour renvoyer l’index il faudra le comparer avec celui recherché, si celui recherché est inférieur, alors celui recherché doit être à l’index à gauche, et s’il est supérieur ou égal, alors à droite.

Allez code :


import (
	"fmt"
	"math/rand"
	"time"
)

const numbersCount = 20
const maximalNumber = 100

func binarySearch(numbers []int, item int, low int, high int) int {
	for high > low {
		center := (low + high) / 2
		if numbers[center] < item { low = center + 1 } else if numbers[center] > item {
			high = center - 1
		} else {
			return center
		}
	}

	if numbers[low] < item {
		return low + 1
	} else {
		return low
	}
}

func main() {
	rand.Seed(time.Now().Unix())
	var numbers [numbersCount]int
	for i := 0; i < numbersCount; i++ {
		numbers[i] = rand.Intn(maximalNumber)
	}
	fmt.Println(numbers)

	for i := 1; i < len(numbers); i++ { searchAreaLastIndex := i - 1 insertNumber := numbers[i] insertIndex := binarySearch(numbers[:], insertNumber, 0, searchAreaLastIndex) for x := searchAreaLastIndex; x >= insertIndex; x-- {
			numbers[x+1] = numbers[x]
		}
		numbers[insertIndex] = insertNumber
	}
	fmt.Println(numbers)
}

Liens

https://gitlab.com/demensdeum/algorithms/-/blob/master/sortAlgorithms/binaryInsertionSort/binaryInsertionSort.go

Sources

https://www.geeksforgeeks.org/binary-insertion- trier/
https://www.youtube.com/watch?v=-OVB5pOZJug

Tri des coquilles

Shell Sort – une variante du tri par insertion avec peignage préliminaire d’un tableau de nombres.

Nous devons nous rappeler comment fonctionne le tri par insertion :

1. Une boucle commence de zéro jusqu’à la fin de la boucle, le tableau est donc divisé en deux parties
2. Pour la partie gauche, une deuxième boucle est lancée, comparant les éléments de droite à gauche, le plus petit élément de droite est déposé jusqu’à ce qu’un plus petit élément de gauche soit trouvé
3. A la fin des deux boucles, on obtient une liste triée

Il était une fois l’informaticien Donald Schell qui se demandait comment améliorer l’algorithme de tri par insertion. Il a également eu l’idée de parcourir d’abord le tableau en deux cycles, mais à une certaine distance, en réduisant progressivement le « peigne » jusqu’à ce qu’il se transforme en un algorithme de tri par insertion régulier. Tout est vraiment si simple, pas d’embûches, aux deux cycles ci-dessus on en ajoute un autre, dans lequel on réduit progressivement la taille du « peigne ». La seule chose que vous devez faire est de vérifier la distance lors de la comparaison afin qu’elle ne dépasse pas le tableau.

Un sujet vraiment intéressant est le choix de la séquence permettant de modifier la longueur de comparaison à chaque itération de la première boucle. C’est intéressant car les performances de l’algorithme en dépendent.

Le tableau des options connues et de la complexité temporelle peut être trouvé ici : https : //fr .wikipedia.org/wiki/Shellsort#Gap_sequences

Différentes personnes ont participé au calcul de la distance idéale ; apparemment, ce sujet les intéressait beaucoup. Ne pourraient-ils pas simplement exécuter Ruby et appeler l’algorithme sort() le plus rapide ?

En général, ces personnes étranges ont écrit des dissertations sur le thème du calcul de la distance/écart du « peigne » pour l’algorithme Shell. J’ai simplement utilisé les résultats de leurs travaux et vérifié 5 types de séquences, Hibbard, Knuth-Pratt, Chiura, Sedgwick.

import time
import random
from functools import reduce
import math

DEMO_MODE = False

if input("Demo Mode Y/N? ").upper() == "Y":
    DEMO_MODE = True

class Colors:
    BLUE = '\033[94m'
    RED = '\033[31m'
    END = '\033[0m'

def swap(list, lhs, rhs):
    list[lhs], list[rhs] = list[rhs], list[lhs]
    return list

def colorPrintoutStep(numbers: List[int], lhs: int, rhs: int):
    for index, number in enumerate(numbers):
        if index == lhs:
            print(f"{Colors.BLUE}", end = "")
        elif index == rhs:
            print(f"{Colors.RED}", end = "")
        print(f"{number},", end = "")
        if index == lhs or index == rhs:
            print(f"{Colors.END}", end = "")
        if index == lhs or index == rhs:
            print(f"{Colors.END}", end = "")
    print("\n")
    input(">")

def ShellSortLoop(numbers: List[int], distanceSequence: List[int]):
    distanceSequenceIterator = reversed(distanceSequence)
    while distance:= next(distanceSequenceIterator, None):
        for sortArea in range(0, len(numbers)):
            for rhs in reversed(range(distance, sortArea + 1)):
                lhs = rhs - distance
                if DEMO_MODE:
                    print(f"Distance: {distance}")
                    colorPrintoutStep(numbers, lhs, rhs)
                if numbers[lhs] > numbers[rhs]:
                    swap(numbers, lhs, rhs)
                else:
                    break

def ShellSort(numbers: List[int]):
    global ShellSequence
    ShellSortLoop(numbers, ShellSequence)

def HibbardSort(numbers: List[int]):
    global HibbardSequence
    ShellSortLoop(numbers, HibbardSequence)

def ShellPlusKnuttPrattSort(numbers: List[int]):
    global KnuttPrattSequence
    ShellSortLoop(numbers, KnuttPrattSequence)

def ShellPlusCiuraSort(numbers: List[int]):
    global CiuraSequence
    ShellSortLoop(numbers, CiuraSequence)

def ShellPlusSedgewickSort(numbers: List[int]):
    global SedgewickSequence
    ShellSortLoop(numbers, SedgewickSequence)

def insertionSort(numbers: List[int]):
    global insertionSortDistanceSequence
    ShellSortLoop(numbers, insertionSortDistanceSequence)

def defaultSort(numbers: List[int]):
    numbers.sort()

def measureExecution(inputNumbers: List[int], algorithmName: str, algorithm):
    if DEMO_MODE:
        print(f"{algorithmName} started")
    numbers = inputNumbers.copy()
    startTime = time.perf_counter()
    algorithm(numbers)
    endTime = time.perf_counter()
    print(f"{algorithmName} performance: {endTime - startTime}")

def sortedNumbersAsString(inputNumbers: List[int], algorithm) -> str:
    numbers = inputNumbers.copy()
    algorithm(numbers)
    return str(numbers)

if DEMO_MODE:
    maximalNumber = 10
    numbersCount = 10
else:
    maximalNumber = 10
    numbersCount = random.randint(10000, 20000)

randomNumbers = [random.randrange(1, maximalNumber) for i in range(numbersCount)]

ShellSequenceGenerator = lambda n: reduce(lambda x, _: x + [int(x[-1]/2)], range(int(math.log(numbersCount, 2))), [int(numbersCount / 2)])
ShellSequence = ShellSequenceGenerator(randomNumbers)
ShellSequence.reverse()
ShellSequence.pop()

HibbardSequence = [
    0, 1, 3, 7, 15, 31, 63, 127, 255, 511, 1023, 2047, 4095,
    8191, 16383, 32767, 65535, 131071, 262143, 524287, 1048575,
    2097151, 4194303, 8388607, 16777215, 33554431, 67108863, 134217727,
    268435455, 536870911, 1073741823, 2147483647, 4294967295, 8589934591
]

KnuttPrattSequence = [
    1, 4, 13, 40, 121, 364, 1093, 3280, 9841, 29524, 88573, 265720, 
    797161, 2391484, 7174453, 21523360, 64570081, 193710244, 581130733, 
    1743392200, 5230176601, 15690529804, 47071589413
]

CiuraSequence = [
            1, 4, 10, 23, 57, 132, 301, 701, 1750, 4376, 
            10941, 27353, 68383, 170958, 427396, 1068491, 
            2671228, 6678071, 16695178, 41737946, 104344866, 
            260862166, 652155416, 1630388541
]

SedgewickSequence = [
            1, 5, 19, 41, 109, 209, 505, 929, 2161, 3905,
            8929, 16001, 36289, 64769, 146305, 260609, 587521, 
            1045505, 2354689, 4188161, 9427969, 16764929, 37730305, 
            67084289, 150958081, 268386305, 603906049, 1073643521, 
            2415771649, 4294770689, 9663381505, 17179475969
]

insertionSortDistanceSequence = [1]

algorithms = {
    "Default Python Sort": defaultSort,
    "Shell Sort": ShellSort,
    "Shell + Hibbard" : HibbardSort,
    "Shell + Prat, Knutt": ShellPlusKnuttPrattSort,
    "Shell + Ciura Sort": ShellPlusCiuraSort,
    "Shell + Sedgewick Sort": ShellPlusSedgewickSort,
    "Insertion Sort": insertionSort
}

for name, algorithm in algorithms.items():
    measureExecution(randomNumbers, name, algorithm)

reference = sortedNumbersAsString(randomNumbers, defaultSort)

for name, algorithm in algorithms.items():
    if sortedNumbersAsString(randomNumbers, algorithm) != reference:
        print("Sorting validation failed")
        exit(1)

print("Sorting validation success")
exit(0)

Dans mon implémentation, pour un ensemble aléatoire de nombres, les écarts les plus rapides sont Sedgwick et Hibbard.

monpy

Je voudrais également mentionner l’analyseur de typage statique pour Python 3 – mypy. Aide à faire face aux problèmes inhérents aux langages à typage dynamique, à savoir qu’il élimine la possibilité de coller quelque chose là où ce n’est pas nécessaire.

Comme le disent les programmeurs expérimentés, “la saisie statique n’est pas nécessaire lorsque vous avez une équipe de professionnels”, un jour nous deviendrons tous des professionnels, nous écrirons du code en pleine unité et compréhension avec les machines, mais pour l’instant vous pouvez utiliser des utilitaires similaires. et les langages typés statiquement.

Liens

https://gitlab.com/demensdeum /algorithms/-/tree/master/sortAlgorithms/shellSort
http://mypy-lang.org/

Sources

https://dl.acm.org/doi/10.1145/368370.368387
https://en.wikipedia.org/wiki/Shellsort
http://rosettacode.org/wiki/Sorting_algorithms/Shell_sort
https://ru.wikipedia.org/wiki/Сортировка_Шелла
https://neerc.ifmo.ru/wiki/index.php?title=Сортировка_Шелла
https://twitter.com/gvanrossum/status/700741601966985216

Tri à double sélection

Tri par sélection double : un sous-type de tri par sélection, il semble qu’il devrait être deux fois plus rapide. L’algorithme Vanilla effectue une double boucle dans la liste des nombres, trouve le nombre minimum et échange les places avec le numéro actuel pointé par la boucle au niveau supérieur. Le tri par double sélection recherche les nombres minimum et maximum, puis remplace les deux chiffres pointés par la boucle au niveau supérieur à – deux chiffres à gauche et à droite. Toute cette orgie se termine lorsque les curseurs des nombres à remplacer se trouvent au milieu de la liste, et par conséquent, des nombres triés sont obtenus à gauche et à droite du centre visuel.
La complexité temporelle de l’algorithme est similaire à celle du tri par sélection – O(n2), mais soi-disant il y a une accélération de 30 %.

État limite

Déjà à ce stade, vous pouvez imaginer le moment d’une collision, par exemple, lorsque le numéro du curseur gauche (le nombre minimum) pointe vers le nombre maximum dans la liste, alors le nombre minimum est réorganisé, le réarrangement du nombre maximum tombe immédiatement en panne. Par conséquent, toutes les implémentations de l’algorithme contiennent la vérification de tels cas et le remplacement des index par des index corrects. Dans mon implémentation, une seule vérification suffisait :

  maximalNumberIndex = minimalNumberIndex;
}

Реализация на Cito

Cito – язык либ, язык транслятор. На нем можно писать для C, C++, C#, Java, JavaScript, Python, Swift, TypeScript, OpenCL C, при этом совершенно ничего не зная про эти языки. Исходный код на языке Cito транслируется в исходный код на поддерживаемых языках, далее можно использовать как библиотеку, либо напрямую, исправив сгенеренный код руками. Эдакий Write once – translate to anything.
Double Selection Sort на cito:

{
    public static int[] sort(int[]# numbers, int length)
    {
        int[]# sortedNumbers = new int[length];
        for (int i = 0; i < length; i++) {
            sortedNumbers[i] = numbers[i];
        }
        for (int leftCursor = 0; leftCursor < length / 2; leftCursor++) {
            int minimalNumberIndex = leftCursor;
            int minimalNumber = sortedNumbers[leftCursor];

            int rightCursor = length - (leftCursor + 1);
            int maximalNumberIndex = rightCursor;
            int maximalNumber = sortedNumbers[maximalNumberIndex];

            for (int cursor = leftCursor; cursor <= rightCursor; cursor++) { int cursorNumber = sortedNumbers[cursor]; if (minimalNumber > cursorNumber) {
                    minimalNumber = cursorNumber;
                    minimalNumberIndex = cursor;
                }
                if (maximalNumber < cursorNumber) {
                    maximalNumber = cursorNumber;
                    maximalNumberIndex = cursor;
                }
            }

            if (leftCursor == maximalNumberIndex) {
                maximalNumberIndex = minimalNumberIndex;
            }

            int fromNumber = sortedNumbers[leftCursor];
            int toNumber = sortedNumbers[minimalNumberIndex];
            sortedNumbers[minimalNumberIndex] = fromNumber;
            sortedNumbers[leftCursor] = toNumber;

            fromNumber = sortedNumbers[rightCursor];
            toNumber = sortedNumbers[maximalNumberIndex];
            sortedNumbers[maximalNumberIndex] = fromNumber;
            sortedNumbers[rightCursor] = toNumber;
        }
        return sortedNumbers;
    }
} 

Liens

https://gitlab.com/demensdeum /algorithms/-/tree/master/sortAlgorithms/doubleSelectionSort
https://github.com/pfusik/cito

Sources

https://www.researchgate.net/publication/330084245_Improved_Double_Selection_Sort_using_Algorithm
http://algolab.valemak.com/selection-double
https://www.geeksforgeeks.org/sorting-algorithm-slightly-improves-selection-sort/

Trier par shaker à cocktail

Tri au shaker à cocktail &#8211 ; le tri shaker, une variante du tri bidirectionnel à bulles.
L’algorithme fonctionne comme suit :

  1. La direction initiale de la recherche dans la boucle est sélectionnée (généralement de gauche à droite)
  2. Ensuite dans la boucle, les nombres sont vérifiés par paires
  3. Si l’élément suivant est plus grand, alors ils sont échangés
  4. Une fois terminé, le processus de recherche recommence avec le sens inversé
  5. La recherche est répétée jusqu’à ce qu’il n’y ait plus de permutations

La complexité temporelle de l’algorithme est similaire à celle d’une bulle ; O(n2).

Exemple d’implémentation en PHP :

<?php

function cocktailShakeSort($numbers)
{
    echo implode(",", $numbers),"\n";
    $direction = false;
    $sorted = false;
    do {
        $direction = !$direction;        
        $firstIndex = $direction == true ? 0 : count($numbers) - 1;
        $lastIndex = $direction == true ? count($numbers) - 1 : 0;
        
        $sorted = true;
        for (
            $i = $firstIndex;
            $direction == true ? $i < $lastIndex : $i > $lastIndex;
            $direction == true ? $i++ : $i--
        ) {
            $lhsIndex = $direction ? $i : $i - 1;
            $rhsIndex = $direction ? $i + 1 : $i;

            $lhs = $numbers[$lhsIndex];
            $rhs = $numbers[$rhsIndex];

            if ($lhs > $rhs) {
                $numbers[$lhsIndex] = $rhs;
                $numbers[$rhsIndex] = $lhs;
                $sorted = false;
            }
        }
    } while ($sorted == false);

    echo implode(",", $numbers);
}

$numbers = [2, 1, 4, 3, 69, 35, 55, 7, 7, 2, 6, 203, 9];
cocktailShakeSort($numbers);

?>

Ссылки

https://gitlab.com/demensdeum/algorithms/-/blob/master/sortAlgorithms/cocktailShakerSort/cocktailShakerSort.php

Источники

https://www.youtube.com/watch?v=njClLBoEbfI
https://www.geeksforgeeks.org/cocktail-sort/
https://rosettacode.org/wiki/Sorting_algorithms/Cocktail_sort

FatBoySize – utilitaire pour afficher la taille des dossiers et des fichiers

FatBoySize – utilitaire d’affichage de la taille des dossiers et des fichiers dans le terminal.
Fonctionne sur n’importe quel système prenant en charge Python 3.

Exécuter : python3 fbs.py
Mode de sortie 1 : python3 fbs.py -v
Mode de sortie 2 : python3 fbs.py --version

Fonctionne uniquement pour le chemin actuellement ouvert dans le terminal.

Exemple de résultat :
python3 ~/Sources/my/fatboysize/fbs.py
.local -> 145.GB
Téléchargements -> 103.GB
.cache -> 37,0 Go
.android -> 11,6 Go
Sources -> 8,63 Go

Comme vous pouvez le constater, le dossier Téléchargements est plutôtshmais volumineux

Liens

https://gitlab.com/demensdeum/fatboysize/ p>