firt commit
|
@ -0,0 +1,3 @@
|
|||
intllib?
|
||||
default?
|
||||
basic_robot?
|
|
@ -0,0 +1,37 @@
|
|||
<!-- --------------------------------------------
|
||||
-- title : Mémo à propos des robots
|
||||
-- author :
|
||||
-- description : Short Guide to Robots Usage
|
||||
-- source : parts from https://wiki.minetest.net/Mods/basic_robot
|
||||
------------------------------------------------- -->
|
||||
|
||||
# Ce qu'il faut savoir à propos des robots
|
||||
|
||||
Quelques informations utiles à propos des robots
|
||||
|
||||
* Les robots peuvent faire de nombreuses choses, ils executent des programmes écrits en 'Lua'.
|
||||
* Il y a aussi une télécommande.
|
||||
|
||||
* Vous pouvez écrire du code dans le spawner ('cpu-box') et l'executer avec le bouton 'start'. Le 'worker' apparaitra alors au dessus du spawner et executera le programme.
|
||||
|
||||
* Le spwaner ressemble à une boite-cpu tandis que le robot est un bloc blanc avec une tête dessinée sur sa face avant et une flèche sur le dessus.
|
||||
|
||||
* Les utilisateurs standard peuvent controler 2 robots. Chaque spawner ne contrôle qu'un robot à la fois.
|
||||
|
||||
|
||||
* Le robot peut stocker des choses dans son inventaire (ex. matériaux creusés), il peut aussi prendre des choses dans cet inventaire (ex. pour produire de l'énérgie)
|
||||
|
||||
* Le robot peut lire et écrire les livres présent dans sa bibliothèque. Ce qui est utile pour accèder facilement à des sous-programmes.
|
||||
|
||||
* Pour rammasser le spawner, son inventaire et sa bibliothèque doivent être vides. Mais le robot est capable de casser le sapwner, son contenu sera alors perdu.
|
||||
|
||||
* Le code d'un programme est executé en boucle, une fois par seconde environ.
|
||||
|
||||
* Le robot à besoin d'énérgie pour effectuer certaines actions telles que creuser, fondre, écraser ou compresser.
|
||||
|
||||
* Si la boucle contient un nombre trop important d'opérations limitées, le code refusera de s'executer et le robot reverra le message d'erreur `robot out of available operations in one step.`.
|
||||
(L'astuce serait d'incrémenter un nombre afin de diviser l'action en étapes. )
|
||||
|
||||
* Le robot ne peut pas léviter au dessus de plus d'un bloc d'air (et ignorera les intructions en ce sens).
|
||||
|
||||
* Si vous partez en laissant votre robot travailler seul, il s'arrêterra et reprendra son programme au DEBUT à votre retour.
|
|
@ -0,0 +1,164 @@
|
|||
<!-- --------------------------------------------
|
||||
-- title : Manuel de robotique
|
||||
-- author :
|
||||
-- description : Guide de l'usage est des comportements des Robots.
|
||||
-- source : parts from https://wiki.minetest.net/Mods/basic_robot
|
||||
------------------------------------------------- -->
|
||||
|
||||
# À propos des robots
|
||||
Ce document présente les comportements des robots ainsi leurs limitations et usages. Pour des instructions détaillées à propos de la programations des robots voir le manuel technique (API)
|
||||
|
||||
## Introduction
|
||||
|
||||
Les robots peuvent se déplacer, détécter des blocs, creuser, construire et plus encore.
|
||||
|
||||
Leurs utilisateurs peuvent écrire des programmes pour le robot dans le langage 'Lua'.
|
||||
Il existe aussi une télécommande.
|
||||
|
||||
Le système consiste en un 'spawner' ('cpu-box') que les joueurs posent sur le sol.
|
||||
Ce spawner propose un formulaire pour contrôler, programmer et faire fonctionner le robot ('worker').
|
||||
Seule cette partie ('worker') du robot se déplace et dispose d'un rayon d'action limité (environ 23 blocs du joueur et du spawner).
|
||||
|
||||
Un clavier du mod basic_machines peut être programmé pour démarrer le robot via un bouton presser par d'autres joueurs et pour recevoir des entrées clavier.
|
||||
C'est utile pour les robot qui executent des actions pour d'autres joueurs (ex. magasin, messagerie, robot parlant, mini-jeu, etc. )
|
||||
|
||||
Sommairement, on peu dire que les robots sont constitué des deux parties mentionnée plus haut :vous pouvez entrer du code dans le spawner ('cpu-box') et l'executer avec le bouton start. Le 'worker' apparaîtra au dessus du spawner et executera le code depuis cette position.
|
||||
|
||||
Il est parfois plus simple d'utiliser le terme 'robot' pour désigner la partie 'worker' dur robot.
|
||||
En disant par exemple : Le spwaner ressemble à une boite-cpu tandis que le robot ressemble à un simple bloc blanc avec une tête sur sa face avant et une flêche sur le dessus.
|
||||
|
||||
## Comment obtenir un robot ?
|
||||
|
||||
Vous pouvez crafter le robot avec sur cristaux de mese au dessus d'une pierre, un lingot d'acier, et une autre pierre
|
||||
Vous pouvrez crafter une télécommande avec un crisal de mese sous un bâton.
|
||||
|
||||
## Le formulaire de contrôle
|
||||
|
||||
Un clic-droit sur le spawner ouvre un formulaire composé de boutons et d'une zone de texte destinée à recevoir le programme du robot.
|
||||
|
||||
* Le boutton "Save" enregistre le programme. Vous devez sauvegardez avant de lancer le programme.
|
||||
|
||||
Note: La sauvegarde n'est pas permanente : une verification syntaxique est réalisées mais aucun fichier n'est enregisté et rammasser le spawner effecera le contenu de cette zone de texte.
|
||||
Copier et collez ce texte ailleurs pour ne pas le perdre.
|
||||
|
||||
* Le boutton "Start" crée un robot-worker (au dessus du spawner) qui execute le programme.
|
||||
|
||||
* Le boutton "Stop" interrompt le programme et supprime le robot-worker.
|
||||
|
||||
* Le champ 'id permet de controller plusieurs robots depuis un spawner.
|
||||
Les joueurs qui ne disposent pas du privilège 'robot' ne peuvent utiliser que 2 robots à la fois. Ce qui signifie qu'ils ne peuvent utiliser que les ids 1 and 2.
|
||||
|
||||
* Le boutton "Inventory" ouvre l'inventaire du robot.
|
||||
L'inventaire contient 8*4 emplacements, la même taille qu'un coffre. Les robots peuvent stocker de choses dans cet inventaire (ex. les matériaux récoltés en creusant), et peuvent aussi prendre des choses dans cet inventaire (ex. pour produire de l'énérgie)
|
||||
|
||||
* Le boutton "Library" ouvre une "bibliothèque". Le robot peut lire et écrire les livres qui s'y trouvent.
|
||||
Cela peut être utilisé pour stocker et accèder facilement à du code depuis le programme.
|
||||
|
||||
* Le boutton "Help" affiche une aide pour les fonctions du robot.
|
||||
|
||||
Utilisez la touche Echap pour quitter le formulaire.
|
||||
|
||||
|
||||
### Notes
|
||||
|
||||
Chaque spawner ne peut activer qu'un robot à la fois.
|
||||
Pour rammasser le spawner, son inventaire et sa bibliothèque doivent être vides. Mais le robot est capable de casser le sapwner, son contenu sera alors perdu.
|
||||
|
||||
Lorsque que le robot est en fonction, un clic-droit dessus oouvrira egalement le formulaire (sans le bouton 'start') vous permettant ainsi de le stoper ou d'accèder à son inventaire.
|
||||
|
||||
Le code du programme est exectuté en boucle répétée environ toutes les secondes
|
||||
|
||||
L'edition du programme d'un robot en fonctionnement ne prendra effet qu'après l'avoir stoppé et relancé.
|
||||
|
||||
|
||||
## Capacitées
|
||||
|
||||
#### * Energie
|
||||
|
||||
Le robot à besoin d'énérgie pour effectuer certaines actions telles que creuser, fondre, écraser ou compresser.
|
||||
Si le robot dispose d'un combustible (ex: un morceau de charbon) dans son inventaire, il peut être programmé pour produire de l'énérgie : `machine.generate_power("default:coal_lump")`
|
||||
Le niveau actuel d'énérgie peut être consulté avec la fonction `machine.energy()`.
|
||||
<!-- Note : Write about upgarde -->
|
||||
|
||||
#### * Limite d'actions
|
||||
|
||||
Comme mentionné plus haut, le programme du robot s'execute en boucle. Cela signifie qu'à moins de recevoir l'instruction de s'arrèter, le robot executera le code encore et encore... aussi longtemps qu'il le peut.
|
||||
|
||||
Pour cette raison, certaines opérations disposent d'un nombre d'execution limité par boucle. Ces actions incluents celles mentionnées plus haut, ainsi que quelques autres (ex. generer de l'energie).
|
||||
<!-- Note : A complete list would be good !-->
|
||||
|
||||
Lorsque cette limite est atteinte, le code refusera de s'executer et le robot reverra le message d'erreur `robot out of available operations in one step.`.
|
||||
Cette limite est parfois appelée 'maxdig' ou 'maxoperations' (le nom de la variable correspondante).
|
||||
<!-- Note : Mabe store maxoperations in self in order to consult it in-gameb -->
|
||||
<!-- Note : Setting the limit to 3 might be a good thing too -->
|
||||
|
||||
La limite par defaut est 2. Ce qui signifie que lorsque le robot démarre, il doit y avoir moins de 2 (=1) opération dans une boucle
|
||||
Par exemple, je ne peux pas executer ce code :
|
||||
|
||||
|
||||
> machine.generate_power("default:jungletree") -- operation limitée
|
||||
> dig.forward() -- operation limitée
|
||||
> move.forward()
|
||||
> dig.up() -- operation limitée
|
||||
|
||||
Selon ce code, le robot devrait générer de l'énergie à partir d'arbre de jungle, creuser vers l'avant, se déplacer vers l'avant, et creuser vers le haut et ... repeter.
|
||||
Mais cela ne fonctionnera pas car 3 actions limitées seraient éxécutées en une boucle.
|
||||
|
||||
L'astuce serait d'incrémenter un nombre afin de diviser l'action en étapes, comme ceci :
|
||||
|
||||
|
||||
> if not i then i = 1 end --initialise i
|
||||
> if i == 1 then machine.generate_power("default:jungletree")
|
||||
> elseif i == 2 then dig.forward()
|
||||
> move.forward()
|
||||
> elseif i == 3 then dig.up()
|
||||
> i = 0
|
||||
> end
|
||||
> i = i + 1
|
||||
|
||||
Dans cet exemple, 'i' prend d'abord la valeur 1 et sa valeurs sera augmenté à chaque boucle (à cause de la dernière ligne)
|
||||
|
||||
La première fois que le code est executé, 'i' vaut 1, de l'énérgie sera générée.
|
||||
|
||||
La seconde fois que le code est executé, 'i' vaudra 2, le robot creusera vers l'avant et se déplacera vers l'avant,
|
||||
|
||||
La troisième fois, 'i' vaudra 3, le robot will creusera vers le haut et réinitialisera la valeur de i à 0 ( de façon à ce qu'après la dernière ligne i sera égal a 1. )
|
||||
|
||||
Donc la quatrième fois que le code sera executé sera identique à la première fois, la cinquième sera comme la seconde, la sixième sera comme la troisième, la septième comme la première... et ainsi de suite.
|
||||
|
||||
|
||||
#### * Gravité
|
||||
|
||||
Bien qu'il ne semble pas être sujet à la gravité, le robot ne peut pas léviter au dessus de plus d'un bloc d'air.
|
||||
|
||||
Si vous lui demandez d'avancer en direction d'une falaise, il ignorera simplement l'operation qui le placerai dans une situation de levitation au dessus de deux blocs d'air ou plus...
|
||||
|
||||
... mais il continuera d'executer le programme en ignorant totalement les conséquences - parfois catastrophiques - de cette étape sautée !
|
||||
|
||||
Si malgré cette prudence, le robot se retouve au dessus du vide, il s'arrêtra simplement et disparaîtra
|
||||
|
||||
#### * Le bruit de l'arbre qui tombe...
|
||||
|
||||
Contrairement à la question étérnelle 'Est ce qu'un arbre qui tombe fait du bruit s'il n'ya personne pour l'entendre?', nous pouvons répondre à celle-ci : 'Est ce q'un robot-worker travaille s'il n'y personne pour le voir ?'.
|
||||
|
||||
Et la réponse est NON.
|
||||
|
||||
Dans minetest, les zone qui ne sont pas peuplées (de joueurs) ne sont pas chargées.
|
||||
Donc, si vous partez loins de votre robot (ou de son spawner), en le laissant travailler, il ne tardera pas à s'arrêter.
|
||||
|
||||
Mais il y a pire ! Lorsque vous reviendrez, la zone où se trouve le robot sera à nouveau chargée et celui-ci reprendra son programme AU DEBUT, sans considération pour la ligne à laquelle il s'était arrèté...
|
||||
|
||||
Ce qui, encore une fois, peut résuter en un désordre catasrophique à cause de quelques lignes sautées.
|
||||
|
||||
Donc, ... voilà
|
||||
|
||||
|
||||
## Conclusion
|
||||
|
||||
L'usage des robots s'apprends par les essais et erreurs.
|
||||
|
||||
Le manuel technique (API) devrait aussi être utile.
|
||||
|
||||
Vous devriez aussi consulter le wiki :
|
||||
and la page du forum : https://forum.minetest.net/feed.php?f=3&t=18345
|
||||
and essayer le serveur Basic_Robot.
|
|
@ -0,0 +1,258 @@
|
|||
<!-- --------------------------------------------
|
||||
-- title : Robot API
|
||||
-- author :
|
||||
-- description : Fonctions et syntaxe pour la programmation des robots
|
||||
-- source : In-Game Help from basic_robot mod
|
||||
-- converted to markdown syntax but still importable into a formspec
|
||||
-- (...still waiting for in-game markdown parser)
|
||||
------------------------------------------------- -->
|
||||
|
||||
# Robot API
|
||||
Ce document présente la plupart des fonctions et la syntaxe pour la programmation des robots.
|
||||
|
||||
### BASIC LUA SYNTAX
|
||||
|
||||
la plupart des syntaxes basique du langage lua sont autorisés.
|
||||
> if x==1 then A else B end
|
||||
> for i = 1, 5 do something end
|
||||
> while i<6 do A; i=i+1; end
|
||||
|
||||
Tableaux:
|
||||
|
||||
> myTable1 = {1,2,3}, myTable2 = {[\"entry1\"]=5, [\"entry2\"]=1}
|
||||
|
||||
( Voir la documentation sur le langage lua pour plus de détails )
|
||||
|
||||
## COMMANDES POUR LES ROBOTS
|
||||
|
||||
### MOVEMENT,DIGGING, PLACING, INVENTORY TAKE/INSERT
|
||||
|
||||
`move.direction()`
|
||||
`turn.left()`, `turn.right()`, `turn.angle(45)`
|
||||
`dig.direction()`
|
||||
`place.direction('default:dirt', optional orientation param)`
|
||||
`read_node.direction()`
|
||||
`insert.direction(item, inventory)`
|
||||
`check_inventory.direction(itemname, inventory, index)`
|
||||
`activate.direction(mode)`
|
||||
`pickup(r)`
|
||||
`craft(item,mode)`
|
||||
`take.direction(item, inventory)`
|
||||
`read_text.direction(stringname,mode)`
|
||||
`write_text.direction(text,mode)`
|
||||
|
||||
---------------------------------
|
||||
|
||||
`move.direction()`
|
||||
Où direction peut être : forward, backward, left,right, up, down
|
||||
La direction forward_down ne fonctionne qu'avec `dig`, `place` et `read_node`
|
||||
|
||||
`turn.left()`, `turn.right()`, `turn.angle(45)`
|
||||
|
||||
`dig.direction()`
|
||||
|
||||
`place.direction('default:dirt', optional orientation param)`
|
||||
|
||||
`read_node.direction()`
|
||||
Renvoie le nom du bloc
|
||||
|
||||
`insert.direction(item, inventory)`
|
||||
Ajoute un objet dans l'inventaire cible depuis l'invetaire du robot
|
||||
|
||||
`check_inventory.direction(itemname, inventory, index)`
|
||||
Inspecte le bloc et renvoie false/true
|
||||
- direction peut être self
|
||||
- si index > 0 , renvoie itemname. Si itemname == '' , test si l'inventaire est vide.
|
||||
|
||||
`activate.direction(mode)`
|
||||
Active le bloc cible
|
||||
|
||||
`pickup(r)`
|
||||
Rammasse tous les objets autours du robot dans un rayon de r. r doit être < 8. Renvoie la liste des objets ou nil.
|
||||
|
||||
`craft(item,mode)`
|
||||
Craft item si les matériaux nécessaires sont présents dans l'inventaire.
|
||||
mode = 1 renvoie la recette
|
||||
|
||||
`take.direction(item, inventory)`
|
||||
Prends item dans l'inventaire cible et l'ajoute à celui du robot.
|
||||
|
||||
`read_text.direction(stringname,mode)`
|
||||
Lit le texte d'un panneau, coffre, ou autre bloc.
|
||||
Paramètre stringname optionnel pour d'autres meta, mode 1 s'il s'agit d'un nombre
|
||||
|
||||
`write_text.direction(text,mode)`
|
||||
Écrit le texte dans le bloc cible (en tant qu'infotext)
|
||||
|
||||
### BOOKS/CODE
|
||||
|
||||
`title,text=book.read(i)`
|
||||
`book.write(i,title,text)`
|
||||
`code.run(text)`
|
||||
`code.set(text)`
|
||||
`find_nodes('default:dirt',3)`
|
||||
|
||||
---------------------------------
|
||||
|
||||
`title,text=book.read(i)`
|
||||
Renvoie titre, contenut du livre à la position i de la bibliothèque
|
||||
|
||||
`book.write(i,title,text)`
|
||||
Ecrit dans le livre à la position i de la bibliothèque
|
||||
|
||||
`code.run(text)`
|
||||
compile et execute le code dans la sandbox
|
||||
|
||||
`code.set(text)`
|
||||
remplace le code actuel du robot
|
||||
|
||||
`find_nodes('default:dirt',3)`
|
||||
renvoie la distance du bloc dans un rayon de 3 ou false s'il n'y en a pas
|
||||
|
||||
### PLAYERS
|
||||
|
||||
`find_player(3)`
|
||||
`attack(target)`
|
||||
`grab(target)`
|
||||
`player.getpos(name)`
|
||||
|
||||
`find_player(3)`
|
||||
finds players in radius 3 around robot and returns list, if none returns nil
|
||||
|
||||
`attack(target)`
|
||||
attempts to attack target player if nearby
|
||||
|
||||
`grab(target)`
|
||||
attempt to grab target player if nearby and returns true if succesful
|
||||
|
||||
`player.getpos(name)`
|
||||
return position of player, player.connected() returns list of players
|
||||
|
||||
### ROBOT
|
||||
|
||||
`say(\"hello\")`
|
||||
`self.listen(0/1)`
|
||||
`speaker, msg = self.listen_msg()`
|
||||
`self.send_mail(target,mail)`
|
||||
`sender,mail = self.read_mail()`
|
||||
`self.pos()`
|
||||
`self.name()`
|
||||
`self.set_properties({textures=.., visual=..,visual_size=.., , )`
|
||||
`self.set_animation(anim_start,anim_end,anim_speed,anim_stand_start)`
|
||||
`self.spam(0/1)`
|
||||
`self.remove()`
|
||||
`self.reset()`
|
||||
`self.spawnpos()`
|
||||
`self.viewdir()`
|
||||
`self.fire(speed, pitch,gravity)`
|
||||
`self.fire_pos()`
|
||||
`self.label(text)`
|
||||
`self.display_text(text,linesize,size)`
|
||||
`self.sound(sample,volume)`
|
||||
|
||||
---------------------------------
|
||||
|
||||
|
||||
`say(\"hello\")`
|
||||
will speak
|
||||
|
||||
`self.listen(0/1)`
|
||||
(de)attaches chat listener to robot
|
||||
|
||||
`speaker, msg = self.listen_msg()`
|
||||
retrieves last chat message if robot listens
|
||||
|
||||
`self.send_mail(target,mail)`
|
||||
sends mail to target robot
|
||||
`sender,mail = self.read_mail()`
|
||||
reads mail, if any
|
||||
`self.pos()`
|
||||
returns table {x=pos.x,y=pos.y,z=pos.z}
|
||||
`self.name()`
|
||||
returns robot name
|
||||
`self.set_properties({textures=.., visual=..,visual_size=.., , )`
|
||||
sets visual appearance
|
||||
`set_animation(anim_start,anim_end,anim_speed,anim_stand_start)`
|
||||
set mesh animation
|
||||
`self.spam(0/1)`
|
||||
(dis)enable message repeat to all
|
||||
`self.remove()`
|
||||
stops program and removes robot object
|
||||
`self.reset()`
|
||||
resets robot position
|
||||
`self.spawnpos()`
|
||||
returns position of spawner block
|
||||
`self.viewdir()`
|
||||
returns vector of view for robot
|
||||
`self.fire(speed, pitch,gravity)`
|
||||
fires a projectile from robot
|
||||
`self.fire_pos()`
|
||||
returns last hit position
|
||||
`self.label(text)`
|
||||
changes robot label
|
||||
`self.display_text(text,linesize,size)`
|
||||
displays text instead of robot face, if no size return text
|
||||
`self.sound(sample,volume)`
|
||||
plays sound named 'sample' at robot location
|
||||
rom is aditional table that can store persistent data, like `rom.x=1`
|
||||
.
|
||||
### KEYBOARD
|
||||
place spawner at coordinates (20i,40j+1,20k) to monitor events
|
||||
|
||||
`keyboard.get()`
|
||||
`keyboard.set(pos,type)`
|
||||
`keyboard.read(pos)`
|
||||
|
||||
---------------------------------
|
||||
|
||||
`keyboard.get()`
|
||||
returns table {x=..,y=..,z=..,puncher = .. , type = .. } for keyboard event
|
||||
`keyboard.set(pos,type)`
|
||||
set key at pos of type 0=air, 1..6, limited to range 10 around
|
||||
`keyboard.read(pos)`
|
||||
return node name at pos
|
||||
|
||||
### TECHNIC FUNCTIONALITY
|
||||
namespace 'machine'. most functions return true or nil, error
|
||||
|
||||
`machine.energy()`
|
||||
`machine.generate_power(fuel, amount)`
|
||||
`machine.smelt(input,amount)`
|
||||
`machine.grind(input)`
|
||||
`machine.compress(input)`
|
||||
`machine.transfer_power(amount,target_robot_name)`
|
||||
|
||||
---------------------------------
|
||||
|
||||
`machine.energy()`
|
||||
displays available energy
|
||||
`machine.generate_power(fuel, amount)`
|
||||
= energy, attempt to generate power from fuel material
|
||||
if amount>0 try generate amount of power using builtin generator - this requires 40 gold/mese/diamonblock upgrades for each 1 amount
|
||||
`machine.smelt(input,amount)`
|
||||
= progress/true. works as a furnace
|
||||
if amount>0 try to use power to smelt - requires 10 upgrades for each 1 amount, energy cost is 1/40*(1+amount)
|
||||
`machine.grind(input)`
|
||||
grinds input material, requires upgrades for harder material
|
||||
`machine.compress(input)`
|
||||
requires upgrades - energy intensive process
|
||||
`machine.transfer_power(amount,target_robot_name)`
|
||||
|
||||
### CRYPTOGRAPHY
|
||||
namespace 'crypto'
|
||||
|
||||
`crypto.encrypt(input,password)`
|
||||
`crypto.decrypt(input,password)`
|
||||
`crypto.scramble(input,randomseed,sgn)`
|
||||
`crypto.basic_hash(input,n)`
|
||||
|
||||
---------------------------------
|
||||
|
||||
`crypto.encrypt(input,password)`
|
||||
returns encrypted text, password is any string
|
||||
`crypto.decrypt(input,password)`
|
||||
attempts to decrypt encrypted text
|
||||
`crypto.scramble(input,randomseed,sgn)`
|
||||
(de)permutes text randomly according to sgn = -1,1
|
||||
`crypto.basic_hash(input,n)`
|
||||
returns simple mod hash from string input within range 0...n-1
|
|
@ -0,0 +1,10 @@
|
|||
<!-- --------------------------------------------
|
||||
-- title : Lua Syntax
|
||||
-- author :
|
||||
-- description : Lua syntax allowed in basic_robot scripts
|
||||
-- source :
|
||||
------------------------------------------------- -->
|
||||
|
||||
|
||||
# Lua Syntax
|
||||
... TODO
|
|
@ -0,0 +1,10 @@
|
|||
<!-- --------------------------------------------
|
||||
-- title : Robot API (admin)
|
||||
-- author :
|
||||
-- description : Robot functions and syntax usable inside robots with admin privilege
|
||||
-- source :
|
||||
------------------------------------------------- -->
|
||||
|
||||
|
||||
# Robot API (Admin)
|
||||
... TODO
|
|
@ -0,0 +1,546 @@
|
|||
-- This mod add a way to import programs for basic_robot
|
||||
--
|
||||
-- TODO : Allow importing file from clients instead or in addition of worldpath
|
||||
|
||||
local modname = minetest.get_current_modname()
|
||||
local modpath = minetest.get_modpath(modname)
|
||||
local worldpath = minetest.get_worldpath()
|
||||
|
||||
-- Load support for intllib.
|
||||
local S, NS = dofile(modpath.."/intllib.lua")
|
||||
-- S = function(s) return s end
|
||||
|
||||
-- Request read/write access to files
|
||||
local ie = minetest.request_insecure_environment()
|
||||
assert(ie, "You must allow `"..modname.."` in `secure.trusted_mods`")
|
||||
|
||||
local private = { }
|
||||
private.open = ie.io.open
|
||||
private.mkdir = ie.core.mkdir
|
||||
|
||||
local brt = {}
|
||||
brt.loglevel = "info"
|
||||
brt.main_categories = {
|
||||
-- Denomination, Prefix, Description
|
||||
{S("Documentation"), "robot_doc", S("Documentation about the robot usage.")},
|
||||
{S("Scripts"), "scripts_index", S("A library of scripts for the robot")},
|
||||
}
|
||||
brt.scripts_categories = {
|
||||
-- Denomination, Prefix, Description
|
||||
{S("Exemples"), "exemple", S("A set of exemples scripts for the robot")},
|
||||
-- {S("Exemples (advanced)"), "exemple_advanced", S("A set of advanced exemples scripts for the robot")},
|
||||
--{S("Testing"), "testing", S("Scripts that may not work and need testing")},
|
||||
{S("Admin"), "admin", S("Scripts that are requiering some admin privilege to run")},
|
||||
}
|
||||
-- Filestructure into worldpath
|
||||
brt.dir = worldpath.."/basic_robot"
|
||||
|
||||
-- Pattern identifiyng files infos
|
||||
brt.patterns = {}
|
||||
brt.patterns.lua = {
|
||||
{ "title", "-- title : " },
|
||||
{ "author", "-- author : " },
|
||||
{ "description", "-- description : "},
|
||||
}
|
||||
|
||||
---------------
|
||||
-- Functions --
|
||||
---------------
|
||||
|
||||
-- Send message to minetest log
|
||||
brt.log = function(text,level)
|
||||
if not level then level = brt.loglevel end
|
||||
minetest.log(level,"["..modname.."] "..text)
|
||||
end
|
||||
|
||||
-- Send notification message to player
|
||||
brt.notify = function(player, text)
|
||||
local name = player:get_player_name()
|
||||
minetest.sound_play({ name = modname.."_blop", gain = 0.2 }, { to_player = name })
|
||||
minetest.chat_send_player(name,text)
|
||||
end
|
||||
|
||||
-- Test if player is admin (has the privs priviliege)
|
||||
brt.player_is_admin = function(player)
|
||||
local name = player:get_player_name()
|
||||
local privs = minetest.get_player_privs(name)
|
||||
if privs and privs.privs then return true
|
||||
else return false
|
||||
end
|
||||
end
|
||||
|
||||
--______________________________________________________________
|
||||
--
|
||||
-- Part 1 : Import scripts from modpath into worlpath
|
||||
--______________________________________________________________
|
||||
|
||||
-- Function : Copy content of a file into an other file
|
||||
brt.copy = function(inpath,outpath)
|
||||
|
||||
local input = private.open(inpath,"r")
|
||||
local output = private.open(outpath,"w")
|
||||
-- If file successfully opened ...
|
||||
if input and output then
|
||||
-- write file content to the one in worldpath
|
||||
local content = input:read("*all")
|
||||
output:write(content)
|
||||
output:close()
|
||||
input:close()
|
||||
else
|
||||
brt.log("Uable to copy content of "..inpath.." to "..outpath)
|
||||
end
|
||||
end
|
||||
|
||||
local scpath = modpath.."/scripts"
|
||||
|
||||
-- Create the basic_robot directory into the world folder
|
||||
private.mkdir(brt.dir)
|
||||
|
||||
-- Copy scripts files gathered in this mod
|
||||
-- Including scripts by rnd contained in basic_robot
|
||||
for _,v in ipairs(brt.scripts_categories) do
|
||||
local n = 1
|
||||
while true do
|
||||
local sc = scpath.."/"..v[2].."_"..n..".lua"
|
||||
local fd = private.open(sc)
|
||||
if not fd then break end
|
||||
fd:close()
|
||||
local dst = brt.dir.."/"..v[2].."_"..n..".lua"
|
||||
brt.copy(sc,dst)
|
||||
n = n + 1
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
--______________________________________________________________
|
||||
--
|
||||
-- Part 2 : Access scripts and doc using a formspec
|
||||
--______________________________________________________________
|
||||
brt.lists = {}
|
||||
brt.lists.scripts = {}
|
||||
brt.lists.docs = {}
|
||||
|
||||
local get_file_infos = function(rep, prefix, ext)
|
||||
local lst = {}
|
||||
local n = 1
|
||||
while true do
|
||||
local sc = rep.."/"..prefix.."_"..n.."."..ext
|
||||
local f = private.open(sc)
|
||||
if not f then break end
|
||||
lst[n] = {}
|
||||
-- This whole thing is pretty heavy...
|
||||
-- Can't I just get line 1,2,3 ?
|
||||
local l = 1
|
||||
for line in f:lines() do
|
||||
local patterns
|
||||
-- Look fot set of patterns specific to this extention
|
||||
if brt.patterns[ext] then patterns = brt.patterns[ext]
|
||||
-- Or default to lua
|
||||
else patterns = brt.patterns.lua end
|
||||
|
||||
-- Look for pattern indicating infos
|
||||
for _,v in ipairs(patterns) do
|
||||
if string.match(line,v[2]) then
|
||||
local k = v[1]
|
||||
lst[n][k] = string.gsub(line,v[2],"")
|
||||
break
|
||||
end
|
||||
end
|
||||
-- Break the loop after 4 lines
|
||||
l = l + 1
|
||||
if l > 4 then break end
|
||||
end
|
||||
f:close()
|
||||
if not lst[n]["title"] then lst[n]["title"] = prefix.."_"..n end
|
||||
-- if not lst[n]["author"] then lst[n]["author"] = "" end
|
||||
lst[n]["path"] = sc
|
||||
n = n + 1
|
||||
end
|
||||
return lst
|
||||
end
|
||||
|
||||
brt.lists_update = function()
|
||||
-- Reinitialize the list of scripts
|
||||
brt.lists.scripts = {}
|
||||
|
||||
-- Update list of scripts to match content of the worldpath rep
|
||||
for _,v in ipairs(brt.scripts_categories) do
|
||||
local listname = v[2]
|
||||
local lst = get_file_infos(brt.dir,listname,"lua")
|
||||
brt.lists.scripts[listname] = lst
|
||||
end
|
||||
end
|
||||
|
||||
brt.lists_make = function()
|
||||
brt.lists_update()
|
||||
local docpath = modpath.."/docs"
|
||||
brt.lists.docs = get_file_infos(docpath,"robot_doc","md")
|
||||
end
|
||||
|
||||
local line_parse = function(line)
|
||||
local op = ","
|
||||
local pt = {
|
||||
{"^# ","#FF9292,"},
|
||||
{"^## ","#9292DB,"},
|
||||
{"^### ","#92FF92,"},
|
||||
{"^#### ","#FFFF00,"},
|
||||
}
|
||||
for _,v in ipairs(pt) do
|
||||
if string.match(line,v[1]) then
|
||||
line = string.gsub(line,v[1],"")
|
||||
op = v[2]
|
||||
break
|
||||
end
|
||||
end
|
||||
line = op .. minetest.formspec_escape(line)
|
||||
return line
|
||||
end
|
||||
|
||||
brt.form = {}
|
||||
|
||||
-- Function : main form content displaying text
|
||||
brt.form.text = function(ref)
|
||||
local ta, filetext, filettable
|
||||
-- Read file to get its content
|
||||
local f = private.open(ref.path)
|
||||
if not f then filetext = S("Sorry, there was a problem while trying to read the file...") end
|
||||
-- If we just want to read the file
|
||||
if ref.read then
|
||||
-- Lets import it as a table
|
||||
filettable = {}
|
||||
local maxlen = 59
|
||||
for line in f:lines() do
|
||||
-- Test if string is too long
|
||||
while true do
|
||||
local len = string.len(line)
|
||||
if len > maxlen then
|
||||
-- Get a resized substring
|
||||
local sub = string.sub(line,1,maxlen)
|
||||
-- Match the longest sequence finishing with a space in this substring
|
||||
local match = string.match(sub, "^(.*%s)")
|
||||
-- If no match, ... split anyways
|
||||
if not match then match = sub end
|
||||
-- Get length of this new substring
|
||||
local matchlen = string.len(match)
|
||||
-- Add it to table
|
||||
filettable[#filettable+1] = line_parse(match)
|
||||
-- Refefine line
|
||||
line = string.sub(line, matchlen + 1, len )
|
||||
else
|
||||
-- TODO Remove comments at the beginning (keep them as tooltip)
|
||||
-- even prepare it for formspec
|
||||
filettable[#filettable+1] = line_parse(line)
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
print(dump(filettable))
|
||||
-- Otherwise we need be able to select and edit and copy and paste, so string it is
|
||||
else filetext = f:read("*all") end
|
||||
f:close()
|
||||
|
||||
-- Display the text
|
||||
-- Docs as readable text
|
||||
if ref.read then
|
||||
-- Table settings
|
||||
ta = "tablecolumns[color;text,align=inline]" --,width=5
|
||||
.. "tableoptions[color=#FFF;background=#494949;highlight=#494949;border=true]"
|
||||
.. "table[0,0;6.7,6;doc;" .. table.concat(filettable, ",") .. ",] "
|
||||
|
||||
-- Script text into a textarea
|
||||
else
|
||||
ta = "textarea[0.4,0;6.7,6.7;script;;"..minetest.formspec_escape(filetext).."]"
|
||||
end
|
||||
-- Add a button to get back to the previous menu
|
||||
ta = ta .. "image_button[1,6.5;0.8,0.8;parent.png;parent;;false;true;parent.png]"
|
||||
-- Hidden field containing name of previous menu
|
||||
ta = ta .. "field[-1,-1;0,0;previous;;"..ref.previous.."]"
|
||||
|
||||
-- TODO : Save button for later implementation of a save
|
||||
-- ta = ta .. "image_button[2,6.5;0.8,0.8;save.png;save;;false;true;save.png]"
|
||||
|
||||
return ta
|
||||
end
|
||||
|
||||
-- Function : main form content displaying a menu
|
||||
brt.form.menu = function(player,ref,i)
|
||||
local description
|
||||
if ref == "main_index" then bck = "."
|
||||
else bck = ".." end
|
||||
|
||||
-- Start the textlist item
|
||||
local menu = "textlist[0,0;6.7,5.5;"..ref..";"..bck
|
||||
local first = true
|
||||
|
||||
-- Main menu
|
||||
if ref == "main_index" then
|
||||
-- Loop through categories
|
||||
for _,v in ipairs(brt.main_categories) do
|
||||
-- Add button
|
||||
local label = v[1]
|
||||
menu = menu .. "," .. minetest.formspec_escape(label)
|
||||
end
|
||||
-- Get description
|
||||
if i and brt.main_categories[i] then description = brt.main_categories[i][3] end
|
||||
|
||||
-- Documentation
|
||||
elseif ref == "robot_doc" then
|
||||
-- Loop through table containing scripts in categories
|
||||
for _,v in ipairs(brt.lists.docs) do
|
||||
local credit = ""
|
||||
-- Add button
|
||||
if v.author and v.author ~= "" then credit = " (by "..v.author..")" end
|
||||
local label = v.title..credit
|
||||
menu = menu .. "," .. minetest.formspec_escape(label)
|
||||
end
|
||||
if i and brt.lists.docs[i] then description = brt.lists.docs[i]["description"] end
|
||||
|
||||
-- Scripts menu
|
||||
elseif ref == "scripts_index" then
|
||||
local has_privs = brt.player_is_admin(player)
|
||||
-- Loop through categories
|
||||
for _,v in ipairs(brt.scripts_categories) do
|
||||
|
||||
-- Skip admin category if player is not admin
|
||||
if ( v[2] == "admin" and not has_privs ) then
|
||||
else
|
||||
-- Add button
|
||||
local label = v[1]
|
||||
menu = menu .. "," .. minetest.formspec_escape(label)
|
||||
end
|
||||
end
|
||||
-- Get description
|
||||
if i and brt.scripts_categories[i] then description = brt.scripts_categories[i][3] end
|
||||
|
||||
-- Any scripts submenu
|
||||
else
|
||||
-- Loop through table containing scripts in categories
|
||||
for _,v in ipairs(brt.lists.scripts[ref]) do
|
||||
local credit = ""
|
||||
-- Add button
|
||||
if v.author and v.author ~= "" then credit = " (by "..v.author..")" end
|
||||
local label = v.title..credit
|
||||
menu = menu .. "," .. minetest.formspec_escape(label)
|
||||
end
|
||||
if i and brt.lists.scripts[ref][i] then description = brt.lists.scripts[ref][i]["description"] end
|
||||
end
|
||||
|
||||
|
||||
-- Close the textlist item
|
||||
if i then menu = menu .. ';]'
|
||||
else menu = menu .. ';1]' end
|
||||
|
||||
-- Display description
|
||||
if description then
|
||||
menu = menu .. "textarea[0.5,5.7;5,2;; ".. minetest.formspec_escape(description) ..";]"
|
||||
end
|
||||
return menu
|
||||
end
|
||||
|
||||
-- Function display the formpec to the player
|
||||
brt.showformspec = function(player,formspec,formname)
|
||||
if not formname then formname = modname..":terminal" end
|
||||
local player_name = player:get_player_name();
|
||||
if not player_name or player_name == "" then return; end
|
||||
|
||||
minetest.show_formspec(player_name,formname,formspec)
|
||||
end
|
||||
|
||||
local deco = default.gui_bg .. default.gui_bg_img
|
||||
local formspec_base = "size[7,7,true]" .. deco
|
||||
local formspec_content
|
||||
local formspec_end = "image_button_exit[6,6.5;0.8,0.8;power.png;poweroff;;false;false;power.png]"
|
||||
-- tooltip[<gui_element_name>;<tooltip_text>;<bgcolor>;<fontcolor>]
|
||||
brt.terminal = function(player,ref,i)
|
||||
-- Set index to default
|
||||
if not ref then ref = "main_index" end
|
||||
-- if not i then i = 1 end
|
||||
|
||||
-- Make or update files lists
|
||||
if #brt.lists.docs < 1 then brt.lists_make()
|
||||
else brt.lists_update() end
|
||||
|
||||
|
||||
if type(ref) == "table" then formspec_content = brt.form.text(ref)
|
||||
else formspec_content = brt.form.menu(player,ref,i)
|
||||
end
|
||||
local formspec = formspec_base .. formspec_content .. formspec_end
|
||||
brt.showformspec(player, formspec)
|
||||
end
|
||||
|
||||
--______________________________________________________________
|
||||
--
|
||||
-- Part 3 : Interpret data from fromspec
|
||||
--______________________________________________________________
|
||||
|
||||
minetest.register_on_player_receive_fields(function(player, formname, fields)
|
||||
|
||||
if formname == modname..":terminal" then
|
||||
print(dump(fields))
|
||||
local name = player:get_player_name()
|
||||
|
||||
-- Get back to previous menu from file view
|
||||
if fields.parent and fields.previous then
|
||||
brt.terminal(player,fields.previous)
|
||||
|
||||
-- Fields came from main menu
|
||||
elseif fields.main_index then
|
||||
-- Get event data
|
||||
local event = minetest.explode_textlist_event(fields.main_index)
|
||||
local i = event.index - 1
|
||||
|
||||
-- Double Click ( ignore first entry )
|
||||
if event.type == "DCL" and i ~= 0 then
|
||||
-- move to selected menu
|
||||
brt.terminal(player,brt.main_categories[i][2])
|
||||
-- Simple Click : Update description
|
||||
else
|
||||
brt.terminal(player,"main_index",i)
|
||||
end
|
||||
|
||||
-- fields came from docs index
|
||||
elseif fields.robot_doc then
|
||||
-- Get event data
|
||||
local event = minetest.explode_textlist_event(fields.robot_doc)
|
||||
local i = event.index - 1
|
||||
|
||||
-- Double Click
|
||||
if event.type == "DCL" then
|
||||
-- First entry : Back to main menu
|
||||
if i == 0 then brt.terminal(player,"main_index")
|
||||
else
|
||||
-- Display selected file
|
||||
local tab = brt.lists.docs[i]
|
||||
tab.previous = "robot_doc"
|
||||
tab.read = true
|
||||
brt.terminal(player,tab)
|
||||
end
|
||||
-- Simple Click : Update description
|
||||
else
|
||||
brt.terminal(player,"robot_doc",i)
|
||||
end
|
||||
|
||||
-- fields came from scripts index
|
||||
elseif fields.scripts_index then
|
||||
-- Get event data
|
||||
local event = minetest.explode_textlist_event(fields.scripts_index)
|
||||
local i = event.index - 1
|
||||
|
||||
-- Double Click :
|
||||
if event.type == "DCL" then
|
||||
-- First entry : Back to main menu
|
||||
if i == 0 then brt.terminal(player,"main_index")
|
||||
-- Otherwise, move to selected categorie
|
||||
else brt.terminal(player,brt.scripts_categories[i][2])
|
||||
end
|
||||
-- Simple Click : Update description
|
||||
else
|
||||
brt.terminal(player,"scripts_index",i)
|
||||
end
|
||||
-- Fields were received from inside a categorie
|
||||
else
|
||||
-- Loop through existing categories to find the right one
|
||||
for _,v in ipairs(brt.scripts_categories) do
|
||||
-- Get categorie name
|
||||
local c = v[2]
|
||||
if fields[c] then
|
||||
-- Get event data
|
||||
local event = minetest.explode_textlist_event(fields[c])
|
||||
local i = event.index - 1
|
||||
|
||||
-- Double Click
|
||||
if event.type == "DCL" then
|
||||
-- First entry : Back to main menu
|
||||
if i == 0 then brt.terminal(player,"scripts_index")
|
||||
else
|
||||
-- Display selected file
|
||||
local tab = brt.lists.scripts[c][i]
|
||||
tab.previous = v[2]
|
||||
brt.terminal(player,tab)
|
||||
--brt.terminal(player,brt.scripts_categories[i][2],i)
|
||||
end
|
||||
-- Simple Click : Update description
|
||||
elseif i ~= 0 then
|
||||
brt.terminal(player,c,i)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end)
|
||||
|
||||
|
||||
--______________________________________________________________
|
||||
--
|
||||
-- Part 4 : Use a block to display fromspec
|
||||
--______________________________________________________________
|
||||
|
||||
|
||||
local texture = {
|
||||
front = modname.."_front.png",
|
||||
back = modname.."_side.png",
|
||||
top = modname.."_side.png",
|
||||
bottom = modname.."_side.png",
|
||||
left = modname.."_side.png",
|
||||
right = modname.."_side.png",
|
||||
}
|
||||
|
||||
minetest.register_node(modname..":terminal",
|
||||
{
|
||||
description = S("Basic robot terminal"),
|
||||
paramtype2 = "facedir",
|
||||
tiles = {
|
||||
texture.top, texture.bottom,
|
||||
texture.left, texture.right,
|
||||
texture.back, texture.front
|
||||
},
|
||||
sounds = default.node_sound_wood_defaults(),
|
||||
groups = { choppy = 3, oddly_breakable_by_hand = 2 },
|
||||
on_rightclick = function(pos, node, player, itemstack, pointedThing)
|
||||
brt.terminal(player);
|
||||
end
|
||||
});
|
||||
|
||||
-- Register Recipe for the terminal block
|
||||
-- ( if default mod is present )
|
||||
if minetest.get_modpath("default") then
|
||||
|
||||
-- Default recipe
|
||||
local r = {}
|
||||
r.c = "default:tin_ingot"
|
||||
r.m = "default:bookshelf"
|
||||
r.t = "default:mese_crystal_fragment"
|
||||
r.d = "default:mese_crystal_fragment"
|
||||
r.l = ""
|
||||
r.r = ""
|
||||
|
||||
-- Changes if mesecons
|
||||
if minetest.get_modpath("mesecons") then
|
||||
r.d = "mesecons:wire_00000000_off"
|
||||
r.t = "mesecons:wire_00000000_off"
|
||||
r.l = "mesecons:wire_00000000_off"
|
||||
r.r = "mesecons:wire_00000000_off"
|
||||
end
|
||||
|
||||
--[[ other changes if dye
|
||||
if minetest.get_modpath("dye") then
|
||||
r.l = "mesecons:magenta"
|
||||
r.t = "mesecons:green"
|
||||
r.r = "mesecons:cyan"
|
||||
end
|
||||
--]]
|
||||
|
||||
minetest.register_craft(
|
||||
{
|
||||
output = modname..":terminal",
|
||||
recipe = {
|
||||
{ r.c, r.t, r.c},
|
||||
{ r.l, r.m, r.r},
|
||||
{ r.c, r.d, r.c},
|
||||
}
|
||||
});
|
||||
|
||||
end
|
||||
|
|
@ -0,0 +1,641 @@
|
|||
-- This mod add a way to import programs for basic_robot
|
||||
--
|
||||
-- TODO : Allow importing file from clients instead or in addition of worldpath
|
||||
|
||||
local modname = minetest.get_current_modname()
|
||||
local modpath = minetest.get_modpath(modname)
|
||||
local worldpath = minetest.get_worldpath()
|
||||
|
||||
-- Load support for intllib.
|
||||
local S, NS = dofile(modpath.."/intllib.lua")
|
||||
-- S = function(s) return s end
|
||||
|
||||
-- Request write access into the world folder
|
||||
local ie = minetest.request_insecure_environment()
|
||||
assert(ie, "You must allow `basic_robot_terminal` in `secure.trusted_mods`")
|
||||
|
||||
local private = { }
|
||||
private.open = ie.io.open
|
||||
private.mkdir = ie.core.mkdir
|
||||
|
||||
local brt = {}
|
||||
brt.loglevel = "info"
|
||||
brt.main_categories = {
|
||||
-- Denomination, Prefix, Description
|
||||
{S("Documentation"), "robot_doc", S("Documentation about the robot usage.")},
|
||||
{S("Scripts"), "scripts_index", S("A library of scripts for the robot")},
|
||||
}
|
||||
brt.scripts_categories = {
|
||||
-- Denomination, Prefix, Description
|
||||
{S("Exemples"), "exemple", S("A set of exemples scripts for the robot")},
|
||||
{S("Exemples (advanced)"), "exemple_advanced", S("A set of advanced exemples scripts for the robot")},
|
||||
{S("Testing"), "testing", S("Scripts that may not work and need testing")},
|
||||
{S("Admin"), "admin", S("Scripts that are requiering some admin privilege to run")},
|
||||
}
|
||||
-- Filestructure into worldpath
|
||||
brt.dir = worldpath.."/basic_robot"
|
||||
|
||||
-- Pattern identifiyng files infos
|
||||
brt.patterns = {}
|
||||
brt.patterns.lua = {
|
||||
{ "title", "-- title : " },
|
||||
{ "author", "-- author : " },
|
||||
{ "description", "-- description : "},
|
||||
}
|
||||
|
||||
---------------
|
||||
-- Functions --
|
||||
---------------
|
||||
|
||||
-- Send message to minetest log
|
||||
brt.log = function(text,level)
|
||||
if not level then level = brt.loglevel end
|
||||
minetest.log(level,"["..modname.."] "..text)
|
||||
end
|
||||
|
||||
-- Send notification message to player
|
||||
brt.notify = function(player, text)
|
||||
local name = player:get_player_name()
|
||||
minetest.sound_play({ name = modname.."_blop", gain = 0.2 }, { to_player = name })
|
||||
minetest.chat_send_player(name,text)
|
||||
end
|
||||
|
||||
-- Test if player is admin (has the privs priviliege)
|
||||
brt.player_is_admin = function(player)
|
||||
local name = player:get_player_name()
|
||||
local privs = minetest.get_player_privs(name)
|
||||
if privs and privs.privs then return true
|
||||
else return false
|
||||
end
|
||||
end
|
||||
|
||||
--______________________________________________________________
|
||||
--
|
||||
-- Part 1 : Import scripts from modpath into worlpath
|
||||
--______________________________________________________________
|
||||
|
||||
-- Function : Copy content of a file into an other file
|
||||
brt.copy = function(inpath,outpath)
|
||||
|
||||
local input = private.open(inpath,"r")
|
||||
local output = private.open(outpath,"w")
|
||||
-- If file successfully opened ...
|
||||
if input and output then
|
||||
-- write file content to the one in worldpath
|
||||
local content = input:read("*all")
|
||||
output:write(content)
|
||||
output:close()
|
||||
input:close()
|
||||
else
|
||||
brt.log("Uable to copy content of "..inpath.." to "..outpath)
|
||||
end
|
||||
end
|
||||
|
||||
local scpath = modpath.."/scripts"
|
||||
|
||||
-- Create the basic_robot directory into the world folder
|
||||
private.mkdir(brt.dir)
|
||||
|
||||
-- Copy scripts files gathered in this mod
|
||||
-- Including scripts by rnd contained in basic_robot
|
||||
for _,v in ipairs(brt.scripts_categories) do
|
||||
local n = 1
|
||||
while true do
|
||||
local sc = scpath.."/"..v[2].."_"..n..".lua"
|
||||
local fd = private.open(sc)
|
||||
if not fd then break end
|
||||
fd:close()
|
||||
local dst = brt.dir.."/"..v[2].."_"..n..".lua"
|
||||
brt.copy(sc,dst)
|
||||
n = n + 1
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
--_____________________________________________________________________
|
||||
--
|
||||
-- Part 2 : Use a chatcommand to import a single script into inventory
|
||||
--_____________________________________________________________________
|
||||
|
||||
--[[
|
||||
minetest.register_chatcommand("terminal", {
|
||||
privs = {
|
||||
-- server = true
|
||||
},
|
||||
params = S("<name>"),
|
||||
description = S("Display the starter kit choice message"),
|
||||
func = function(name, param)
|
||||
if not param or param == "" then
|
||||
param = name
|
||||
end
|
||||
local player = minetest.get_player_by_name(param)
|
||||
brt.terminal(player)
|
||||
end
|
||||
})
|
||||
--]]
|
||||
|
||||
--______________________________________________________________
|
||||
--
|
||||
-- Part 2 : Save a book to a file
|
||||
--______________________________________________________________
|
||||
|
||||
brt.save_to_book = function(player,text,confirm)
|
||||
local msg
|
||||
|
||||
-- Check that there is something to save
|
||||
if not text then
|
||||
msg = S("Sorry, no content to save...")
|
||||
brt.notify(player,msg
|
||||
return
|
||||
end
|
||||
|
||||
-- Check that there is a book to save to
|
||||
local inv = player:get_inventory()
|
||||
local has_empty_book = inv:contains_item("main", "default:book")
|
||||
if not has_empty_book then
|
||||
msg = S("You need at least one empty book in your inventory")
|
||||
brt.notify(player,msg
|
||||
return
|
||||
end
|
||||
|
||||
-- Ask confirmation
|
||||
if comfirm then
|
||||
msg = S("Copy script into an empty book from your inventory ?")
|
||||
local formspec = "size[7,3,true]" .. deco
|
||||
.."textarea[0.4,0;6.4,2;;"..minetest.formspec_escape(msg)..";]"
|
||||
.. "button_exit[1.5,2;1.5,1;confirm;"..S("Yes").."]"
|
||||
.. "button_exit[4.5,2;1.5,1;abort;"..S("No").."]"
|
||||
-- hidden area containing the text to copy
|
||||
.."field[-1,-1;0,0;previous;;"..fields.script.."]"
|
||||
local formname = modname..":confirm_save_to_book"
|
||||
|
||||
brt.showformspec(player,formspec,formname)
|
||||
return
|
||||
|
||||
else -- Make the copy
|
||||
local written_book =
|
||||
inv:remove_item("main", "default:book")
|
||||
if inv:room_for_item ("main", "default:book_written") then
|
||||
add_item(listname, stack)
|
||||
|
||||
end
|
||||
--______________________________________________________________
|
||||
--
|
||||
-- Part 2 : Access scripts and doc using a formspec
|
||||
--______________________________________________________________
|
||||
brt.lists = {}
|
||||
brt.lists.scripts = {}
|
||||
brt.lists.docs = {}
|
||||
|
||||
local get_file_infos = function(rep, prefix, ext)
|
||||
local lst = {}
|
||||
local n = 1
|
||||
while true do
|
||||
local sc = rep.."/"..prefix.."_"..n.."."..ext
|
||||
local f = private.open(sc)
|
||||
if not f then break end
|
||||
lst[n] = {}
|
||||
-- This whole thing is pretty heavy...
|
||||
-- Can't I just get line 1,2,3 ?
|
||||
local l = 1
|
||||
for line in f:lines() do
|
||||
local patterns
|
||||
-- Look fot set of patterns specific to this extention
|
||||
if brt.patterns[ext] then patterns = brt.patterns[ext]
|
||||
-- Or default to lua
|
||||
else patterns = brt.patterns.lua end
|
||||
|
||||
-- Look for pattern indicating infos
|
||||
for _,v in ipairs(patterns) do
|
||||
if string.match(line,v[2]) then
|
||||
local k = v[1]
|
||||
lst[n][k] = string.gsub(line,v[2],"")
|
||||
break
|
||||
end
|
||||
end
|
||||
-- Break the loop after 4 lines
|
||||
l = l + 1
|
||||
if l > 4 then break end
|
||||
end
|
||||
f:close()
|
||||
if not lst[n]["title"] then lst[n]["title"] = listname.."_"..n end
|
||||
-- if not lst[n]["author"] then lst[n]["author"] = "" end
|
||||
lst[n]["path"] = sc
|
||||
n = n + 1
|
||||
end
|
||||
return lst
|
||||
end
|
||||
|
||||
brt.lists_update = function()
|
||||
-- Reinitialize the list of scripts
|
||||
brt.lists.scripts = {}
|
||||
|
||||
-- Update list of scripts to match content of the worldpath rep
|
||||
for _,v in ipairs(brt.scripts_categories) do
|
||||
local listname = v[2]
|
||||
local lst = get_file_infos(brt.dir,listname,"lua")
|
||||
brt.lists.scripts[listname] = lst
|
||||
end
|
||||
end
|
||||
|
||||
brt.lists_make = function()
|
||||
brt.lists_update()
|
||||
local docpath = modpath.."/docs"
|
||||
brt.lists.docs = get_file_infos(docpath,"robot_doc","md")
|
||||
end
|
||||
|
||||
local line_parse = function(line)
|
||||
local op = ","
|
||||
local pt = {
|
||||
{"^# ","#FF9292,"},
|
||||
{"^## ","#9292DB,"},
|
||||
{"^### ","#92FF92,"},
|
||||
{"^#### ","#FFFF00,"},
|
||||
}
|
||||
for _,v in ipairs(pt) do
|
||||
if string.match(line,v[1]) then
|
||||
line = string.gsub(line,v[1],"")
|
||||
op = v[2]
|
||||
break
|
||||
end
|
||||
end
|
||||
line = op .. minetest.formspec_escape(line)
|
||||
return line
|
||||
end
|
||||
|
||||
brt.form = {}
|
||||
|
||||
-- Function : main form content displaying text
|
||||
brt.form.text = function(ref)
|
||||
local ta, filetext, filettable
|
||||
-- Read file to get its content
|
||||
local f = private.open(ref.path)
|
||||
if not f then filetext = S("Sorry, there was a problem while trying to read the file...") end
|
||||
-- If we just want to read the file
|
||||
if ref.read then
|
||||
-- Lets import it as a table
|
||||
filettable = {}
|
||||
maxlen = 59
|
||||
for line in f:lines() do
|
||||
-- Test if string is too long
|
||||
while true do
|
||||
local len = string.len(line)
|
||||
if len > maxlen then
|
||||
-- Get a resized substring
|
||||
local sub = string.sub(line,1,maxlen)
|
||||
-- Match the longest sequence finishing with a space in this substring
|
||||
local match = string.match(sub, "^(.*%s)")
|
||||
-- If no match, ... split anyways
|
||||
if not match then match = sub end
|
||||
-- Get length of this new substring
|
||||
local matchlen = string.len(match)
|
||||
-- Add it to table
|
||||
filettable[#filettable+1] = line_parse(match)
|
||||
-- Refefine line
|
||||
line = string.sub(line, matchlen + 1, len )
|
||||
else
|
||||
-- TODO Remove comments at the beginning (keep them as tooltip)
|
||||
-- even prepare it for formspec
|
||||
filettable[#filettable+1] = line_parse(line)
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
print(dump(filettable))
|
||||
-- Otherwise we need be able to select and edit and copy and paste, so string it is
|
||||
else filetext = f:read("*all") end
|
||||
f:close()
|
||||
|
||||
-- Display the text
|
||||
-- Docs as readable text
|
||||
if ref.read then
|
||||
-- Table settings
|
||||
ta = "tablecolumns[color;text,align=inline]" --,width=5
|
||||
.. "tableoptions[color=#FFF;background=#494949;highlight=#494949;border=true]"
|
||||
.. "table[0,0;6.7,6;doc;" .. table.concat(filettable, ",") .. ",] "
|
||||
|
||||
-- Script text into a textarea
|
||||
else
|
||||
ta = "textarea[0.4,0;6.7,6.7;script;;"..minetest.formspec_escape(filetext).."]"
|
||||
end
|
||||
-- Add a button to get back to the previous menu
|
||||
ta = ta .. "image_button[1,6.5;0.8,0.8;parent.png;parent;;false;true;parent.png]"
|
||||
-- Hidden field containing name of previous menu
|
||||
ta = ta .. "field[-1,-1;0,0;previous;;"..ref.previous.."]"
|
||||
|
||||
-- TODO : Save button for later implementation of a save to a book option
|
||||
-- ta = ta .. "image_button[2,6.5;0.8,0.8;save.png;save;;false;true;save.png]"
|
||||
|
||||
return ta
|
||||
end
|
||||
|
||||
-- Function : main form content displaying a menu
|
||||
brt.form.menu = function(player,ref,i)
|
||||
local description
|
||||
if ref == "main_index" then bck = "."
|
||||
else bck = ".." end
|
||||
|
||||
-- Start the textlist item
|
||||
local menu = "textlist[0,0;6.7,5.5;"..ref..";"..bck
|
||||
local first = true
|
||||
|
||||
-- Main menu
|
||||
if ref == "main_index" then
|
||||
-- Loop through categories
|
||||
for _,v in ipairs(brt.main_categories) do
|
||||
-- Add button
|
||||
local label = v[1]
|
||||
menu = menu .. "," .. minetest.formspec_escape(label)
|
||||
end
|
||||
-- Get description
|
||||
if i and brt.main_categories[i] then description = brt.main_categories[i][3] end
|
||||
|
||||
-- Documentation
|
||||
elseif ref == "robot_doc" then
|
||||
-- Loop through table containing scripts in categories
|
||||
for _,v in ipairs(brt.lists.docs) do
|
||||
local credit = ""
|
||||
-- Add button
|
||||
if v.author and v.author ~= "" then credit = " (by "..v.author..")" end
|
||||
local label = v.title..credit
|
||||
menu = menu .. "," .. minetest.formspec_escape(label)
|
||||
end
|
||||
if i and brt.lists.docs[i] then description = brt.lists.docs[i]["description"] end
|
||||
|
||||
-- Scripts menu
|
||||
elseif ref == "scripts_index" then
|
||||
local has_privs = brt.player_is_admin(player)
|
||||
-- Loop through categories
|
||||
for _,v in ipairs(brt.scripts_categories) do
|
||||
|
||||
-- Skip admin category if player is not admin
|
||||
if ( v[2] == "admin" and not has_privs ) then
|
||||
else
|
||||
-- Add button
|
||||
local label = v[1]
|
||||
menu = menu .. "," .. minetest.formspec_escape(label)
|
||||
end
|
||||
end
|
||||
-- Get description
|
||||
if i and brt.scripts_categories[i] then description = brt.scripts_categories[i][3] end
|
||||
|
||||
-- Any scripts submenu
|
||||
else
|
||||
-- Loop through table containing scripts in categories
|
||||
for _,v in ipairs(brt.lists.scripts[ref]) do
|
||||
local credit = ""
|
||||
-- Add button
|
||||
if v.author and v.author ~= "" then credit = " (by "..v.author..")" end
|
||||
local label = v.title..credit
|
||||
menu = menu .. "," .. minetest.formspec_escape(label)
|
||||
end
|
||||
if i and brt.lists.scripts[ref][i] then description = brt.lists.scripts[ref][i]["description"] end
|
||||
end
|
||||
|
||||
|
||||
-- Close the textlist item
|
||||
if i then menu = menu .. ';]'
|
||||
else menu = menu .. ';1]' end
|
||||
|
||||
-- Display description
|
||||
if description then
|
||||
menu = menu .. "textarea[0.5,5.7;5,2;; ".. minetest.formspec_escape(description) ..";]"
|
||||
end
|
||||
return menu
|
||||
end
|
||||
|
||||
-- Function display the formpec to the player
|
||||
brt.showformspec = function(player,formspec,formname)
|
||||
if not formname then formname = modname..":terminal" end
|
||||
local player_name = player:get_player_name();
|
||||
if not player_name or player_name == "" then return; end
|
||||
|
||||
minetest.show_formspec(player_name,formname,formspec)
|
||||
end
|
||||
|
||||
local deco = default.gui_bg .. default.gui_bg_img
|
||||
local formspec_base = "size[7,7,true]" .. deco
|
||||
local formspec_content
|
||||
local formspec_end = "image_button_exit[6,6.5;0.8,0.8;power.png;poweroff;;false;false;power.png]"
|
||||
-- tooltip[<gui_element_name>;<tooltip_text>;<bgcolor>;<fontcolor>]
|
||||
brt.terminal = function(player,ref,i)
|
||||
-- Set index to default
|
||||
if not ref then ref = "main_index" end
|
||||
-- if not i then i = 1 end
|
||||
|
||||
-- Make or update files lists
|
||||
if #brt.lists.docs < 1 then brt.lists_make()
|
||||
else brt.lists_update() end
|
||||
|
||||
|
||||
if type(ref) == "table" then formspec_content = brt.form.text(ref)
|
||||
else formspec_content = brt.form.menu(player,ref,i)
|
||||
end
|
||||
local formspec = formspec_base .. formspec_content .. formspec_end
|
||||
brt.showformspec(player, formspec)
|
||||
end
|
||||
|
||||
--______________________________________________________________
|
||||
--
|
||||
-- Part 3 : Interpret data from fromspec
|
||||
--______________________________________________________________
|
||||
|
||||
minetest.register_on_player_receive_fields(function(player, formname, fields)
|
||||
|
||||
if formname == modname..":terminal" then
|
||||
print(dump(fields))
|
||||
local name = player:get_player_name()
|
||||
|
||||
-- Get back to previous menu from file view
|
||||
if fields.parent and fields.previous then
|
||||
brt.terminal(player,fields.previous)
|
||||
|
||||
-- TODO save text into an empty book
|
||||
elseif fields.save_to_book then
|
||||
--local msg = S("Sorry, the 'save to book' functionnality hasn't been implemented yet...")
|
||||
-- .."\n"..S("... just use the old CRTL+C, CTRL+V, for now")
|
||||
--brt.notify(player,msg)
|
||||
brt.save_to_book(player,fields)
|
||||
|
||||
-- Fields came from main menu
|
||||
elseif fields.main_index then
|
||||
-- Get event data
|
||||
local event = minetest.explode_textlist_event(fields.main_index)
|
||||
local i = event.index - 1
|
||||
|
||||
-- Double Click ( ignore first entry )
|
||||
if event.type == "DCL" and i ~= 0 then
|
||||
-- move to selected menu
|
||||
brt.terminal(player,brt.main_categories[i][2])
|
||||
-- Simple Click : Update description
|
||||
else
|
||||
brt.terminal(player,"main_index",i)
|
||||
end
|
||||
|
||||
-- fields came from docs index
|
||||
elseif fields.robot_doc then
|
||||
-- Get event data
|
||||
local event = minetest.explode_textlist_event(fields.robot_doc)
|
||||
local i = event.index - 1
|
||||
|
||||
-- Double Click
|
||||
if event.type == "DCL" then
|
||||
-- First entry : Back to main menu
|
||||
if i == 0 then brt.terminal(player,"main_index")
|
||||
else
|
||||
-- Display selected file
|
||||
local tab = brt.lists.docs[i]
|
||||
tab.previous = "robot_doc"
|
||||
tab.read = true
|
||||
brt.terminal(player,tab)
|
||||
end
|
||||
-- Simple Click : Update description
|
||||
else
|
||||
brt.terminal(player,"robot_doc",i)
|
||||
end
|
||||
|
||||
-- fields came from scripts index
|
||||
elseif fields.scripts_index then
|
||||
-- Get event data
|
||||
local event = minetest.explode_textlist_event(fields.scripts_index)
|
||||
local i = event.index - 1
|
||||
|
||||
-- Double Click :
|
||||
if event.type == "DCL" then
|
||||
-- First entry : Back to main menu
|
||||
if i == 0 then brt.terminal(player,"main_index")
|
||||
-- Otherwise, move to selected categorie
|
||||
else brt.terminal(player,brt.scripts_categories[i][2])
|
||||
end
|
||||
-- Simple Click : Update description
|
||||
else
|
||||
brt.terminal(player,"scripts_index",i)
|
||||
end
|
||||
-- Fields were received from inside a categorie
|
||||
else
|
||||
-- Loop through existing categories to find the right one
|
||||
for _,v in ipairs(brt.scripts_categories) do
|
||||
-- Get categorie name
|
||||
local c = v[2]
|
||||
if fields[c] then
|
||||
-- Get event data
|
||||
local event = minetest.explode_textlist_event(fields[c])
|
||||
local i = event.index - 1
|
||||
|
||||
-- Double Click
|
||||
if event.type == "DCL" then
|
||||
-- First entry : Back to main menu
|
||||
if i == 0 then brt.terminal(player,"scripts_index")
|
||||
else
|
||||
-- Display selected file
|
||||
local tab = brt.lists.scripts[c][i]
|
||||
tab.previous = v[2]
|
||||
brt.terminal(player,tab)
|
||||
--brt.terminal(player,brt.scripts_categories[i][2],i)
|
||||
end
|
||||
-- Simple Click : Update description
|
||||
elseif i ~= 0 then
|
||||
brt.terminal(player,c,i)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end)
|
||||
|
||||
--______________________________________________________________
|
||||
--
|
||||
-- Part 4 : Register a chat command tool
|
||||
--______________________________________________________________
|
||||
|
||||
-- TODO : chat command to list available scripts and to display one, or copy to a book
|
||||
--[[
|
||||
-- Chat command : display the formspec
|
||||
minetest.register_chatcommand("terminal", {
|
||||
privs = {
|
||||
-- server = true
|
||||
},
|
||||
params = S("<name>"),
|
||||
description = S("Display the starter kit choice message"),
|
||||
func = function(name, param)
|
||||
if not param or param == "" then
|
||||
param = name
|
||||
end
|
||||
local player = minetest.get_player_by_name(param)
|
||||
brt.terminal(player)
|
||||
end
|
||||
})
|
||||
--]]
|
||||
--______________________________________________________________
|
||||
--
|
||||
-- Part 5 : Use a block to display fromspec
|
||||
--______________________________________________________________
|
||||
|
||||
|
||||
local texture = {
|
||||
front = modname.."_front.png",
|
||||
back = modname.."_side.png",
|
||||
top = modname.."_side.png",
|
||||
bottom = modname.."_side.png",
|
||||
left = modname.."_side.png",
|
||||
right = modname.."_side.png",
|
||||
}
|
||||
|
||||
minetest.register_node(modname..":terminal",
|
||||
{
|
||||
description = S("Basic robot terminal"),
|
||||
paramtype2 = "facedir",
|
||||
tiles = {
|
||||
texture.top, texture.bottom,
|
||||
texture.left, texture.right,
|
||||
texture.back, texture.front
|
||||
},
|
||||
sounds = default.node_sound_wood_defaults(),
|
||||
groups = { choppy = 3, oddly_breakable_by_hand = 2 },
|
||||
on_rightclick = function(pos, node, player, itemstack, pointedThing)
|
||||
brt.terminal(player);
|
||||
end
|
||||
});
|
||||
|
||||
-- Register Recipe for the terminal block
|
||||
-- ( if default mod is present )
|
||||
if minetest.get_modpath("default") then
|
||||
|
||||
-- Default recipe
|
||||
local r = {}
|
||||
r.c = "default:tin_ingot"
|
||||
r.m = "default:bookshelf"
|
||||
r.t = "default:mese_crystal_fragment"
|
||||
r.d = "default:mese_crystal_fragment"
|
||||
r.l = ""
|
||||
r.r = ""
|
||||
|
||||
-- Changes if mesecons
|
||||
if minetest.get_modpath("mesecons") then
|
||||
r.d = "mesecons:wire_00000000_off"
|
||||
r.t = "mesecons:wire_00000000_off"
|
||||
r.l = "mesecons:wire_00000000_off"
|
||||
r.r = "mesecons:wire_00000000_off"
|
||||
end
|
||||
|
||||
--[[ other changes if dye
|
||||
if minetest.get_modpath("dye") then
|
||||
r.l = "mesecons:magenta"
|
||||
r.t = "mesecons:green"
|
||||
r.r = "mesecons:cyan"
|
||||
end
|
||||
--]]
|
||||
|
||||
minetest.register_craft(
|
||||
{
|
||||
output = modname..":terminal",
|
||||
recipe = {
|
||||
{ r.c, r.t, r.c},
|
||||
{ r.l, r.m, r.r},
|
||||
{ r.c, r.d, r.c},
|
||||
}
|
||||
});
|
||||
|
||||
end
|
|
@ -0,0 +1,45 @@
|
|||
|
||||
-- Fallback functions for when `intllib` is not installed.
|
||||
-- Code released under Unlicense <http://unlicense.org>.
|
||||
|
||||
-- Get the latest version of this file at:
|
||||
-- https://raw.githubusercontent.com/minetest-mods/intllib/master/lib/intllib.lua
|
||||
|
||||
local function format(str, ...)
|
||||
local args = { ... }
|
||||
local function repl(escape, open, num, close)
|
||||
if escape == "" then
|
||||
local replacement = tostring(args[tonumber(num)])
|
||||
if open == "" then
|
||||
replacement = replacement..close
|
||||
end
|
||||
return replacement
|
||||
else
|
||||
return "@"..open..num..close
|
||||
end
|
||||
end
|
||||
return (str:gsub("(@?)@(%(?)(%d+)(%)?)", repl))
|
||||
end
|
||||
|
||||
local gettext, ngettext
|
||||
if minetest.get_modpath("intllib") then
|
||||
if intllib.make_gettext_pair then
|
||||
-- New method using gettext.
|
||||
gettext, ngettext = intllib.make_gettext_pair()
|
||||
else
|
||||
-- Old method using text files.
|
||||
gettext = intllib.Getter()
|
||||
end
|
||||
end
|
||||
|
||||
-- Fill in missing functions.
|
||||
|
||||
gettext = gettext or function(msgid, ...)
|
||||
return format(msgid, ...)
|
||||
end
|
||||
|
||||
ngettext = ngettext or function(msgid, msgid_plural, n, ...)
|
||||
return format(n==1 and msgid or msgid_plural, ...)
|
||||
end
|
||||
|
||||
return gettext, ngettext
|
|
@ -0,0 +1,28 @@
|
|||
# Basic Robot Terminal
|
||||
|
||||
*Documentation is not up to date* and need to be remade
|
||||
|
||||
This add a craftable terminal unit related to the [basic_robot](https://github.com/ac-minetest/basic_robot/) mod.
|
||||
It can be used to consult documentation about robots and browse some scripts contained into the world folder
|
||||
|
||||
I made it to use in a subgame that is meant to be used in surival mode, without any admin privileges and without basic machines.
|
||||
Scripts and documentation included are focused on only basic robot
|
||||
|
||||
-[x] Fromspec window to browse scripts
|
||||
-[x] Also provide some documentation
|
||||
-[x] Terminal block to acces the formspec
|
||||
-[ ] Documentation book (and treasurer support)
|
||||
-[ ] Book storage into the terminal block
|
||||
-[ ] Copy script to a book from the terminal
|
||||
-[ ] Save scripts to external file
|
||||
|
||||
As a bonus : The documentation is stored in markdown format in the docs folder and is also converted into one single html file (robot_doc.html)
|
||||
|
||||
|
||||
|
||||
|
||||
License:
|
||||
I tryed to credit scripts and other things I took a the beginings of files
|
||||
Some texture files are from the basic_robot mod
|
||||
If I forgot something tell me I will add it
|
||||
and for the rest : code LGPL 2.1 media CC BY-SA 3.0
|
|
@ -0,0 +1,346 @@
|
|||
|
||||
<h1>Robot Documentation</h1>
|
||||
<a name='top'></a><ul>
|
||||
<li><a href="#robot_doc_1.md"> About Robots</a>
|
||||
<br> - Guide to understand Robots usages and behaviors</li>
|
||||
<li><a href="#robot_doc_2.md"> About Robots - The short version</a>
|
||||
<br> - Short Guide to Robots Usage</li>
|
||||
<li><a href="#robot_doc_3.md"> Robot API</a>
|
||||
<br> - Robot functions and syntax usable inside robots</li>
|
||||
<li><a href="#robot_doc_4.md"> Lua Syntax</a>
|
||||
<br> - Lua syntax allowed in basic_robot scripts</li>
|
||||
<li><a href="#robot_doc_5.md"> Robot API (admin)</a>
|
||||
<br> - Robot functions and syntax usable inside robots with admin privilege</li>
|
||||
</ul>
|
||||
|
||||
<hr>
|
||||
<a name="robot_doc_1.md"></a><a href="#top">-- Return to Index --</a><br>
|
||||
<!--
|
||||
-- title : About Robots
|
||||
-- author :
|
||||
-- description : Guide to understand Robots usages and behaviors
|
||||
-- source : parts from https://wiki.minetest.net/Mods/basic_robot
|
||||
-->
|
||||
|
||||
<p>This document present robots behaviors, limitations and usages
|
||||
for detailled functions and syntax, see 'api<em>basic</em>robot'</p>
|
||||
|
||||
<h1>About Robots</h1>
|
||||
|
||||
<h2>Introduction</h2>
|
||||
|
||||
<p>The robot can move around, sense the blocks, dig, build, and much more.</p>
|
||||
|
||||
<p>Users can write programs for the bot in Lua. There is also a remote control.</p>
|
||||
|
||||
<p>The system uses a spawner ('cpu-box') that the player has to place on the ground.
|
||||
This spawner presents a form for controlling, programming and running the robot ("worker").
|
||||
Only this worker moves around, and it has a limited range (about 23 nodes from player and spawner).</p>
|
||||
|
||||
<p>A keypad from the mod basic_machines can be set up to start a robot with a button-push by other players, and for keyboard-entry.
|
||||
This is useful for robots that perform a task for other players, e.g. as a shop, mailbox, chatbot, game, etc. </p>
|
||||
|
||||
<p>Mainly, robots are constituted of the two part mentionned above : you can write code in the spawner ('cpu-box') and run it with the sart button. The 'worker' will appear above the spawns block and execute the code from there.</p>
|
||||
|
||||
<p>It is someting easier to call 'robot' the worker part of the robot, saying think like :
|
||||
The spwaner looks like a ... cpu-box while the robot looks like a simple box, with a face on its frontside, and an arrow on top. </p>
|
||||
|
||||
<h2>How to get it ?</h2>
|
||||
|
||||
<p>You can craft a robot using 6 mese crystals atop a stone, a steel ingot, and another stone.
|
||||
You can craft a remote control using mese crystal under a stick</p>
|
||||
|
||||
<h2>Controlling Form</h2>
|
||||
|
||||
<p>Rightclicking the spawner opens a form with buttons and a textbox for writing a program.</p>
|
||||
|
||||
<ul>
|
||||
<li><p>Button "Save" saves the program. This must be done before starting a program. </p>
|
||||
|
||||
<p>Note: Saving is not permanent - there is no file to save to, just a quick syntax-check.
|
||||
Also, picking up the spawner clears the textarea with the code.
|
||||
So, use cut and paste to save your code to a texteditor, such as notepad. </p></li>
|
||||
<li><p>Button "Start" creates a robot-worker (on top of the spawner) that starts executing the program. </p></li>
|
||||
<li><p>Button "Stop" stops the program and removes the robot-worker. </p></li>
|
||||
<li><p>Entryfield id for controlling several robots.
|
||||
Players without the priv 'robot' are only allowed to run 2 robots at the same time.
|
||||
This means they can only use the ids 1 and 2.</p></li>
|
||||
<li><p>Button "Inventory" opens the inventory of the bot.
|
||||
The inventory has 8*4 slots, same size as a chest. Robot can store thing to its inventory (e.g. materials gathered while digging), they can also take thing from it (e.g. to produce energy)</p></li>
|
||||
<li><p>Button "Library" opens a "bookshelf". The robot can read and write books here.
|
||||
This can be used for program code, input and output. </p></li>
|
||||
<li><p>Button "Help" shows some helptext.</p></li>
|
||||
</ul>
|
||||
|
||||
<p>Press ESC to exit the form. </p>
|
||||
|
||||
<h3>Notes</h3>
|
||||
|
||||
<p>Each spawner can only run one bot at a time.
|
||||
To pick up the spawner, its inventory and library must be empty (like a chest).</p>
|
||||
|
||||
<p>While the robots is running, rightclicking it will also open the form
|
||||
(without the Start-button), so you can stop it, and access its inventory. </p>
|
||||
|
||||
<p>The code of a program is executed repeatedly, about once every second.</p>
|
||||
|
||||
<p>After editing the program of a running robot won't affect it until it is stopped and restarted. </p>
|
||||
|
||||
<h2>Capabilities</h2>
|
||||
|
||||
<ul>
|
||||
<li>Energy</li>
|
||||
</ul>
|
||||
|
||||
<p>The robot require energy to perform some actions like digging, smelting, grinding or compressing.
|
||||
If the robot as the required fuel in its inventory (e.g. a coal lump ), it can be programmed to produce energy using <code>machine.generate_power("default:coal_lump")</code>
|
||||
Energy level can be consulted using <code>machine.energy()</code>.
|
||||
<!-- Note : Write about upgarde --></p>
|
||||
|
||||
<ul>
|
||||
<li>Maxdig</li>
|
||||
</ul>
|
||||
|
||||
<p>As said above, the robot program runs in a loop. This means that unless specificly asking it to stop, the robot will execute the code again and again and again... as long as it can.
|
||||
Because of this, some operations can only be performed a limited number of time per run. These actions includes the ones mentionned above along with some other (including generating energy).
|
||||
<!-- Note : A complete list would be good !--></p>
|
||||
|
||||
<p>If this limit is reached, the code won't execute and the robot will return this error message : <code>robot out of available operations in one step.</code>
|
||||
The limit is sometime called 'maxdig' or 'maxoperations' (as the name of the variable defining it).
|
||||
<!-- Note : Mabe store maxoperations in self in order to consult it in-game-->
|
||||
<!-- Note : Setting the limit to 3 might be a good thing too-->
|
||||
The default for this limit is 2. This means that when the robot is starting, there must be less than 2 operations in one loop.
|
||||
For exemple, I cannot run this programm :</p>
|
||||
|
||||
<blockquote>
|
||||
<p>machine.generate_power("default:jungletree") -- limited operation
|
||||
dig.forward() -- limited operation
|
||||
move.forward()
|
||||
dig.up() -- limited operation</p>
|
||||
</blockquote>
|
||||
|
||||
<p>According to this code, the robot should, generate some power from some jungle tree, dig forward, move foward, dig upwards and ... repeat itself. But it won't do any of that because that 3 limited operations would be performed in one execution</p>
|
||||
|
||||
<p>The trick would be increment a number to divide the steps, like this :</p>
|
||||
|
||||
<blockquote>
|
||||
<p>if not i then i = 1 end --initialize i
|
||||
if i == 1 then machine.generate_power("default:jungletree") <br />
|
||||
elseif i == 2 then dig.forward() <br />
|
||||
move.forward()
|
||||
elseif i == 3 then dig.up() <br />
|
||||
i = 0
|
||||
end <br />
|
||||
i = i + 1 </p>
|
||||
</blockquote>
|
||||
|
||||
<p>In this exemple 'i' is set to 1 initially, and its value will be upped each time code is executed (because of the last line).
|
||||
The first time the code is executed, 'i' is set to one, power will be generated <br />
|
||||
The second time the code is executed, 'i' will be egal 2, the robot will dig forward and move forward,
|
||||
The third time the code is executed, 'i' will be egal 3, the robot will dig up and reset the value of i to 0 so that after last line, it will be egal 1.
|
||||
So the fourth time is like the first, the fifth is like the second, the sixth is like the third, and so on...</p>
|
||||
|
||||
<ul>
|
||||
<li>Gravity</li>
|
||||
</ul>
|
||||
|
||||
<p>Altough it doesn't see to be subject to gravity, the robot cannot hover over more than one block of air.
|
||||
If asked to move toward a cliff, the robot will simply ignore the operation that would put him in a situation with too much void under...
|
||||
... but it will continue executing its program regardless of the - sometme catastophic - consequences of this skipped step !</p>
|
||||
|
||||
<p><check> If despite this beahior, the robot ends up above more than one block of air, it will simply stop and disapear.</p>
|
||||
|
||||
<ul>
|
||||
<li>Sound of a falling tree</li>
|
||||
</ul>
|
||||
|
||||
<p>Quite unlike the eternal question 'Does a falling tree make sound if there is no one to hear it ?', we can answer this one : 'Does a robot worker works when there is no one around to see it ?'. And the answer is NO.</p>
|
||||
|
||||
<p>In minetest, areas that are not populated (with players), are unloaded.
|
||||
So if you wander off leaving your robot to its work, it will soon stop working. But there is worse !
|
||||
When you come back, you robot will be loaded again and will restart its program AT THE BEGINNING, regardless to were it stopped. Resulting (again) in a giant mess due to a few steps skipped. So, ... Be warned</p>
|
||||
|
||||
<hr>
|
||||
<a name="robot_doc_2.md"></a><a href="#top">-- Return to Index --</a><br>
|
||||
<!--
|
||||
-- title : About Robots - The short version
|
||||
-- author :
|
||||
-- description : Short Guide to Robots Usage
|
||||
-- source : parts from https://wiki.minetest.net/Mods/basic_robot
|
||||
-->
|
||||
|
||||
<h1>Things to know about Robots</h1>
|
||||
|
||||
<p>A few things to know about robots, summarized in a short lists</p>
|
||||
|
||||
<ul>
|
||||
<li>The robot can do a lot of things, They execute programs written in Lua.</li>
|
||||
<li><p>There is also a remote control.</p></li>
|
||||
<li><p>You can write code in the spawner ('cpu-box') and run it with the 'start' button. The 'worker' will appear above the spawns block and execute the code from there.</p></li>
|
||||
<li><p>The spwaner looks like a ... cpu-box while the robot looks like a simple box, with a face on its frontside, and an arrow on top. </p></li>
|
||||
<li><p>Standard users are allowed to control 2 robots. Each spawner can only run one bot at a time.</p></li>
|
||||
<li><p>Robot can store things to its inventory (e.g. materials gathered while digging), they can also take thing from it (e.g. to produce energy)</p></li>
|
||||
<li><p>The robot can read and write books contained in their 'library'. This can be used for program code, input and output. </p></li>
|
||||
<li><p>To pick up the spawner, its inventory and library must be empty (like a chest).</p></li>
|
||||
<li><p>The code of a program is executed repeatedly, about once every second.</p></li>
|
||||
<li><p>The robot require energy to perform some actions like digging, smelting, grinding or compressing.</p></li>
|
||||
<li><p>Some operations can only be performed a limited number of time per run otherwise the robot will return this error message : <code>robot out of available operations in one step.</code></p></li>
|
||||
</ul>
|
||||
|
||||
<p>(The trick would be increment a number to divide the steps, like this )</p>
|
||||
|
||||
<ul>
|
||||
<li><p>The robot cannot hover over more than one block of air (and will skip the instruction to do so).</p></li>
|
||||
<li><p>If you wander off leaving your robot to its work, it will stop and restart its program AT THE BEGINNING when you come back.</p></li>
|
||||
</ul>
|
||||
|
||||
<hr>
|
||||
<a name="robot_doc_3.md"></a><a href="#top">-- Return to Index --</a><br>
|
||||
<!--
|
||||
-- title : Robot API
|
||||
-- author :
|
||||
-- description : Robot functions and syntax usable inside robots
|
||||
-- source : In-Game Help from basic_robot mod
|
||||
-- converted to mod syntax but still importable into a text area
|
||||
-- (...still waiting for in-game markdown parser)
|
||||
-->
|
||||
|
||||
<h1>Robot API</h1>
|
||||
|
||||
<p>This document present most of functions and syntax usable inside robots
|
||||
For details about behaviors, limitations and usages, see 'api<em>basic</em>robot'</p>
|
||||
|
||||
<h3>BASIC LUA SYNTAX</h3>
|
||||
|
||||
<p>most of basic lua syntax allowed</p>
|
||||
|
||||
<blockquote>
|
||||
<p>if x==1 then A else B end
|
||||
for i = 1, 5 do something end
|
||||
while i<6 do A; i=i+1; end</p>
|
||||
</blockquote>
|
||||
|
||||
<p>arrays:</p>
|
||||
|
||||
<blockquote>
|
||||
<p>myTable1 = {1,2,3}, myTable2 = {[\"entry1\"]=5, [\"entry2\"]=1}</p>
|
||||
</blockquote>
|
||||
|
||||
<p>access table entries with myTable1[1] or myTable2.entry1 or myTable2[\"entry1\"]</p>
|
||||
|
||||
<p>( See lua syntax for more detailled informations )</p>
|
||||
|
||||
<h2>ROBOT COMMANDS</h2>
|
||||
|
||||
<h3>MOVEMENT,DIGGING, PLACING, INVENTORY TAKE/INSERT</h3>
|
||||
|
||||
<p><code>move.direction()</code> where direction is forward, backward, left,right, up, down), forward<em>down direction only works with dig, place and read</em>node
|
||||
<code>turn.left()</code>, <code>turn.right()</code>, <code>turn.angle(45)</code>
|
||||
<code>dig.direction()</code>
|
||||
<code>place.direction(\"default:dirt\", optional orientation param)</code>
|
||||
<code>read_node.direction()</code> tells you names of nodes
|
||||
<code>insert.direction(item, inventory)</code> inserts item from robot inventory to target inventory
|
||||
<code>check_inventory.direction(itemname, inventory, index)</code> looks at node and returns false/true, direction can be self,
|
||||
if index>0 it returns itemname. if itemname == \"\" it checks if inventory empty
|
||||
<code>activate.direction(mode)</code> activates target block
|
||||
<code>pickup(r)</code> picks up all items around robot in radius r<8 and returns list or nil
|
||||
<code>craft(item,mode)</code> crafts item if required materials are present in inventory. mode = 1 returns recipe
|
||||
<code>take.direction(item, inventory)</code> takes item from target inventory into robot inventory
|
||||
<code>read_text.direction(stringname,mode)</code> reads text of signs, chests and other blocks, optional stringname for other meta, mode 1 read number
|
||||
<code>write_text.direction(text,mode)</code> writes text to target block as infotext</p>
|
||||
|
||||
<h3>BOOKS/CODE</h3>
|
||||
|
||||
<p><code>title,text=book.read(i)</code> returns title,contents of book at i-th position in library
|
||||
<code>book.write(i,title,text)</code> writes book at i-th position at spawner library
|
||||
<code>code.run(text)</code> compiles and runs the code in sandbox
|
||||
<code>code.set(text)</code> replaces current bytecode of robot
|
||||
<code>find_nodes(\"default:dirt\",3)</code> returns distance to node in radius 3 around robot, or false if none</p>
|
||||
|
||||
<h3>PLAYERS</h3>
|
||||
|
||||
<p><code>find_player(3)</code> finds players in radius 3 around robot and returns list, if none returns nil
|
||||
<code>attack(target)</code> attempts to attack target player if nearby
|
||||
<code>grab(target)</code> attempt to grab target player if nearby and returns true if succesful
|
||||
<code>player.getpos(name)</code> return position of player, player.connected() returns list of players</p>
|
||||
|
||||
<h3>ROBOT</h3>
|
||||
|
||||
<p><code>say(\"hello\")</code> will speak
|
||||
<code>self.listen(0/1)</code> (de)attaches chat listener to robot
|
||||
<code>speaker, msg = self.listen_msg()</code> retrieves last chat message if robot listens
|
||||
<code>self.send_mail(target,mail)</code> sends mail to target robot
|
||||
<code>sender,mail = self.read_mail()</code> reads mail, if any
|
||||
<code>self.pos()</code> returns table {x=pos.x,y=pos.y,z=pos.z}
|
||||
<code>self.name()</code> returns robot name
|
||||
<code>self.set_properties({textures=.., visual=..,visual_size=.., , )</code> sets visual appearance
|
||||
<code>set_animation(anim_start,anim_end,anim_speed,anim_stand_start)</code> set mesh animation
|
||||
<code>self.spam(0/1)</code> (dis)enable message repeat to all
|
||||
<code>self.remove()</code> stops program and removes robot object
|
||||
<code>self.reset()</code> resets robot position
|
||||
<code>self.spawnpos()</code> returns position of spawner block
|
||||
<code>self.viewdir()</code> returns vector of view for robot
|
||||
<code>self.fire(speed, pitch,gravity)</code> fires a projectile from robot
|
||||
<code>self.fire_pos()</code> returns last hit position
|
||||
<code>self.label(text)</code> changes robot label
|
||||
<code>self.display_text(text,linesize,size)</code> displays text instead of robot face, if no size return text
|
||||
<code>self.sound(sample,volume)</code> plays sound named 'sample' at robot location
|
||||
rom is aditional table that can store persistent data, like <code>rom.x=1</code>
|
||||
.</p>
|
||||
|
||||
<h3>KEYBOARD</h3>
|
||||
|
||||
<p>place spawner at coordinates (20i,40j+1,20k) to monitor events</p>
|
||||
|
||||
<p><code>keyboard.get()</code> returns table {x=..,y=..,z=..,puncher = .. , type = .. } for keyboard event
|
||||
<code>keyboard.set(pos,type)</code> set key at pos of type 0=air, 1..6, limited to range 10 around
|
||||
<code>keyboard.read(pos)</code> return node name at pos</p>
|
||||
|
||||
<h3>TECHNIC FUNCTIONALITY</h3>
|
||||
|
||||
<p>namespace 'machine'. most functions return true or nil, error</p>
|
||||
|
||||
<p><code>machine.energy()</code> displays available energy
|
||||
<code>machine.generate_power(fuel, amount)</code> = energy, attempt to generate power from fuel material
|
||||
if amount>0 try generate amount of power using builtin generator - this requires 40 gold/mese/diamonblock upgrades for each 1 amount
|
||||
<code>machine.smelt(input,amount)</code> = progress/true. works as a furnace
|
||||
if amount>0 try to use power to smelt - requires 10 upgrades for each 1 amount, energy cost is 1/40*(1+amount)
|
||||
<code>machine.grind(input)</code> grinds input material, requires upgrades for harder material
|
||||
<code>machine.compress(input)</code> requires upgrades - energy intensive process
|
||||
<code>machine.transfer_power(amount,target_robot_name)</code></p>
|
||||
|
||||
<h2>CRYPTOGRAPHY</h2>
|
||||
|
||||
<p>namespace 'crypto'</p>
|
||||
|
||||
<p><code>crypto.encrypt(input,password)</code> returns encrypted text, password is any string
|
||||
<code>crypto.decrypt(input,password)</code> attempts to decrypt encrypted text
|
||||
<code>crypto.scramble(input,randomseed,sgn)</code> (de)permutes text randomly according to sgn = -1,1
|
||||
<code>crypto.basic_hash(input,n)</code> returns simple mod hash from string input within range 0...n-1</p>
|
||||
|
||||
<hr>
|
||||
<a name="robot_doc_4.md"></a><a href="#top">-- Return to Index --</a><br>
|
||||
<!--
|
||||
-- title : Lua Syntax
|
||||
-- author :
|
||||
-- description : Lua syntax allowed in basic_robot scripts
|
||||
-- source :
|
||||
-->
|
||||
|
||||
<h3>Lua Syntax</h3>
|
||||
|
||||
<p>... TODO</p>
|
||||
|
||||
<hr>
|
||||
<a name="robot_doc_5.md"></a><a href="#top">-- Return to Index --</a><br>
|
||||
<!--
|
||||
-- title : Robot API (admin)
|
||||
-- author :
|
||||
-- description : Robot functions and syntax usable inside robots with admin privilege
|
||||
-- source :
|
||||
-->
|
||||
|
||||
<h3>Robot API (Admin)</h3>
|
||||
|
||||
<p>... TODO</p>
|
||||
|
|
@ -0,0 +1,181 @@
|
|||
-- title : avl_tree~
|
||||
-- author : rnd
|
||||
-- description :
|
||||
-- source : https://github.com/ac-minetest/basic_robot/
|
||||
--
|
||||
|
||||
-- 1. MEMORY MANAGEMENT using array with addresses
|
||||
-- note: this is all done "automatic" in c with malloc
|
||||
|
||||
mem = {};
|
||||
mem.size = 10; -- how many available addresses in memory for each of internally used arrays
|
||||
mem.freestk = {}; -- stack of free addresses in memory
|
||||
mem.stkidx = mem.size;
|
||||
for i = 1,mem.size do mem.freestk[i]=mem.size-i+1 end -- so: freestk = {memsize, memsize-1,...., 2, 1 } and head is at last one
|
||||
|
||||
mem.allocate = function() -- pop free spot from stack
|
||||
if mem.stkidx>0 then
|
||||
mem.stkidx = mem.stkidx -1
|
||||
return mem.freestk[mem.stkidx+1]
|
||||
end
|
||||
end
|
||||
|
||||
mem.free = function(addr) -- release spot and mark it as free
|
||||
if mem.stkidx>=mem.size then -- cant release anymore, all is free
|
||||
return
|
||||
end
|
||||
mem.stkidx = mem.stkidx +1;
|
||||
mem.freestk[mem.stkidx ] = addr;
|
||||
end
|
||||
|
||||
|
||||
-- 2. BALANCED BINARY SEARCH TREES USING POINTERS created with above MEMORY MANAGEMENT
|
||||
|
||||
idx = mem.allocate();
|
||||
|
||||
tree = {};
|
||||
tree.data = { root = 0, count = 0, -- current root, element count
|
||||
left = {0}, parent = {0}, right = {0}, -- links
|
||||
key = {0}, -- value
|
||||
heightl = {0}, heightr = {0} -- needed for balancing
|
||||
};
|
||||
-- root: idx of root element, count: how many elements in tree
|
||||
-- 6 arrays with data for node stored at address i in memory:
|
||||
-- left[i] == left child, right[i] = right child, parent[i] = parent, key[i] = stored value, if value left[i]== 0 then no left child...
|
||||
-- heightl[i] = height of left subtree, heightr[i] = height of right subtree
|
||||
-- NOTE: initially a root node is added
|
||||
|
||||
tree.insert = function (value)
|
||||
local data = tree.data;
|
||||
local idx = data.root;
|
||||
if data.count == 0 then data.key[idx] = key; data.count = 1 return true end -- just set root key and exit
|
||||
|
||||
local key;
|
||||
local cidx; -- child idx
|
||||
|
||||
|
||||
while (true) do -- insert into tree by walking deeper and doing comparisons
|
||||
|
||||
key = data.key[idx];
|
||||
if value<key then
|
||||
cidx = data.left[idx];
|
||||
else
|
||||
cidx = data.right[idx];
|
||||
end
|
||||
|
||||
if cidx == 0 then -- we hit nonexistent child, lets insert here
|
||||
cidx = mem.allocate();
|
||||
if not cidx then return "out of space" end
|
||||
data.count = data.count+1
|
||||
data.key[cidx] = value;
|
||||
data.parent[cidx]=idx;data.left[cidx]=0;data.right[cidx]=0;
|
||||
data.heightl[cidx]=0;data.heightr[cidx]=0;
|
||||
|
||||
-- update all heights for parents and find possible violation of balance property
|
||||
local vidx = 0; -- index where violation happens
|
||||
local vdir = 0; -- type of violation: 1: in left tree , 2: in right tree
|
||||
while (idx~=0) do
|
||||
if data.left[idx] == cidx then
|
||||
data.heightl[idx]=data.heightl[idx]+1
|
||||
else
|
||||
data.heightr[idx]=data.heightr[idx]+1;
|
||||
end
|
||||
|
||||
if data.heightl[idx]>data.heightr[idx]+1 then
|
||||
vidx = idx; vdir = 1
|
||||
elseif data.heightr[idx]>data.heightl[idx]+1 then
|
||||
vidx = idx; vdir = 2
|
||||
end
|
||||
|
||||
cidx = idx; -- set new child
|
||||
idx = data.parent[idx]; -- set new parent
|
||||
end
|
||||
if vidx~=0 then
|
||||
say("violation vidx " .. vidx .. " direction " .. vdir)
|
||||
-- TO DO: apply internal tree rotation to restore balance
|
||||
if vdir == 1 then -- left violation
|
||||
--[[ need to reconnect 3 nodes:
|
||||
D <- vidx C
|
||||
/ \ / \
|
||||
C E => A D
|
||||
/ \ / \
|
||||
A B B E
|
||||
CHANGES:
|
||||
C: new parent is old parent of D, new children are A,D
|
||||
B: new parent D,
|
||||
D: new parent is C, new children are B,E
|
||||
--]]
|
||||
local Didx = vidx; local Dpar = data.parent[Didx];
|
||||
local Cidx = data.left[Didx];
|
||||
local Bidx = data.right[Cidx];
|
||||
local Aidx = data.left[Cidx];
|
||||
|
||||
data.parent[Cidx] = Dpar;data.left[Cidx] = Aidx;data.right[Cidx] = Didx;
|
||||
data.parent[Bidx] = Didx;
|
||||
data.parent[Didx] = Cidx;data.left[Didx] = Bidx;
|
||||
|
||||
|
||||
else -- right violation
|
||||
--[[ need to reconnect 3 nodes:
|
||||
B <-vidx C
|
||||
/ \ / \
|
||||
A C => B E
|
||||
/ \ / \
|
||||
D E A D
|
||||
CHANGES:
|
||||
B: new parent C, new children A,D
|
||||
C: new parent is old parent of B, new children are B,E
|
||||
D: new parent is B
|
||||
--]]
|
||||
local Bidx = vidx; local Bpar = data.parent[Bidx];
|
||||
local Cidx = data.right[Bidx];
|
||||
local Didx = data.left[Cidx];
|
||||
|
||||
data.parent[Bidx] = Cidx;data.right[Bidx] = data.left[Cidx]
|
||||
data.parent[Cidx] = Bpar;data.left[Cidx] = Bidx;
|
||||
data.parent[Didx] = Bidx;
|
||||
end
|
||||
end
|
||||
|
||||
else
|
||||
-- we go deeper
|
||||
idx = cidx;
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
tree.find = function(value)
|
||||
local idx = data.root;
|
||||
while (idx~=0) do
|
||||
key = data.key[idx];
|
||||
if key == value then return idx end
|
||||
if value<key then
|
||||
idx = data.left[idx];
|
||||
else
|
||||
idx = data.right[idx];
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
tree.next = function(idx)
|
||||
local right = data.right[idx];
|
||||
local nidx = 0;
|
||||
if right~=0 then
|
||||
--TO DO: smallest idx in right subtree
|
||||
else
|
||||
if data.parent[idx]==0 then
|
||||
return 0
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
tree.remove = function(idx)
|
||||
--TO DO :
|
||||
-- 1.find next element
|
||||
-- put it where idx was and move subtree..
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
end
|
|
@ -0,0 +1,89 @@
|
|||
-- title : perm2cycles
|
||||
-- author : rnd
|
||||
-- description :
|
||||
-- source : https://github.com/ac-minetest/basic_robot/
|
||||
--
|
||||
|
||||
if not perm2cycles then
|
||||
|
||||
perm2cycles = function(perm)
|
||||
local n = #perm;
|
||||
local i = 1; -- already reached number
|
||||
local ret = {};
|
||||
local visited = {};
|
||||
local step = 0;
|
||||
|
||||
while (true) do
|
||||
local cycle = {i}
|
||||
local j=i;
|
||||
|
||||
while (true) do
|
||||
step = step +1
|
||||
if step > 2*n then return {} end
|
||||
j=perm[j];
|
||||
visited[j] = 1;
|
||||
if j and j~=cycle[1] then cycle[#cycle+1]=j else break end
|
||||
end
|
||||
|
||||
i=n+1;
|
||||
for k=1,n do -- find smallest non visited yet
|
||||
if not visited[k] and k<i then i = k end
|
||||
end
|
||||
|
||||
ret[#ret+1] = cycle;
|
||||
if i == n+1 then return ret end
|
||||
end
|
||||
end
|
||||
|
||||
random_permute = function(arr)
|
||||
local n = #arr;
|
||||
for i = n,3,-1 do
|
||||
local j = math.random(i);
|
||||
local tmp = arr[j]; arr[j] = arr[i]; arr[i] = tmp;
|
||||
end
|
||||
end
|
||||
|
||||
get_permutations = function(n)
|
||||
free = {}; --[1] = {stack of free for 1 : head, a1,..., a_head}
|
||||
isfree = {}; -- [i]=1 if item i free, 0 if not
|
||||
current = 1; --{1,..., current element, ... , n}
|
||||
selection = {};
|
||||
local data = free[1]; for i=1,n do data[i]=i isfree[i]=1 end data[0] = n;
|
||||
|
||||
--1. pick free spot from free stack ( if not possible backtrack ) and move on onto next element ( if not possible stay at this one)
|
||||
-- backtrack: current--
|
||||
local data = free[current];
|
||||
if data[0]<1 then -- backtrack
|
||||
isfree[selection[current]] = 1;
|
||||
current = current - 1;
|
||||
if current <=0 then return end -- exhausted all options
|
||||
else
|
||||
local i = data[data[0]]; -- free possibility
|
||||
selection[current] = i; isfree[i] = 0;
|
||||
data[0]=data[0]-1; -- pop the stack
|
||||
--try move forward
|
||||
if current<n then
|
||||
current = current+1;
|
||||
-- get new free spots for new current
|
||||
local data = free[current]; data = {};
|
||||
for i = 1,n do if isfree[i] == 1 then data[#data+1]=i; break end end;
|
||||
data[0]=#data;
|
||||
if data[0] == 0 then -- nothing free, backtrack
|
||||
isfree[selection[current]] = 1;
|
||||
current = current - 1;
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
arr2string = function(arr)
|
||||
return string.gsub(_G.dump(arr),"\n","")
|
||||
end
|
||||
|
||||
local arr = {}; for i =1,10 do arr[i] =i end; random_permute(arr);
|
||||
local cycles = perm2cycles(arr);
|
||||
say("random permutation = " .. arr2string(arr) .. " => cycles = " .. arr2string(cycles) )
|
||||
|
||||
end
|
|
@ -0,0 +1,110 @@
|
|||
-- title : radix_sort
|
||||
-- author : rnd
|
||||
-- description :
|
||||
-- source : https://github.com/ac-minetest/basic_robot/
|
||||
--
|
||||
|
||||
if not integer_sort then
|
||||
-- sorts input according to keys, changes ordering to permutation corresponding to new order
|
||||
integer_sort = function(input, keys, ordering,temp_ordering, m) -- input, keys same length n, m = how many keys, O(2*n+m)
|
||||
|
||||
local n = #input;
|
||||
local freq = {} -- frequencies of keys
|
||||
local kpos = {} -- position of keys in sorted array
|
||||
|
||||
for i=1,n do -- count how many occurences - O(n)
|
||||
local key = keys[ordering[i]]+1;
|
||||
freq[key]=(freq[key] or 0)+1
|
||||
temp_ordering[i]=ordering[i];
|
||||
end
|
||||
|
||||
local curpos = 1;kpos[1]=1;
|
||||
for i =1,m-1 do -- determine positions of keys in final sorted array - O(m)
|
||||
curpos = curpos + (freq[i] or 0);
|
||||
kpos[i+1]=curpos -- {2=3, 3 = 6,..., n-1 = 321}
|
||||
end
|
||||
|
||||
-- actually place values here
|
||||
for i = 1,n do -- O(n)
|
||||
local key = keys[temp_ordering[i]]+1;
|
||||
local pos = kpos[key];
|
||||
ordering[pos] = temp_ordering[i];
|
||||
kpos[key]=kpos[key]+1; -- move to next spot for that key place
|
||||
end
|
||||
end
|
||||
|
||||
permutate = function(input,ordering)
|
||||
local output = {};
|
||||
for i =1,#input do
|
||||
output[i] = input[ordering[i]]
|
||||
end
|
||||
return output
|
||||
end
|
||||
|
||||
get_digits = function(Num,d,base) -- number, how many digits, what base
|
||||
local digits = {};
|
||||
local num = Num;
|
||||
local r;
|
||||
for i = 1, d do
|
||||
r = num % base;
|
||||
digits[#digits+1] = r;
|
||||
num = (num-r)/base
|
||||
end
|
||||
return digits
|
||||
end
|
||||
|
||||
dumparr = function(array)
|
||||
if _G.type(array) ~= "table" then return array end
|
||||
local ret = "{";
|
||||
for i =1,#array-1 do
|
||||
ret = ret .. dumparr(array[i]) .. ","
|
||||
end
|
||||
ret = ret .. dumparr(array[#array])
|
||||
return ret .. "}"
|
||||
end
|
||||
|
||||
|
||||
radix_sort = function(input,d,base) -- array of numbers; base is also number of keys = m, d = how many steps of sorting = number of digits for single number
|
||||
|
||||
out = out .."\nRADIX SORT\n\narray to be sorted " .. dumparr(input)
|
||||
|
||||
local n = #input;
|
||||
local ordering = {}; local temp_ordering = {}; for i = 1, n do ordering[i]=i end
|
||||
local keys = {};
|
||||
local keylist = {};
|
||||
for i = 1,n do
|
||||
keylist[i] = get_digits(input[i],d,base)
|
||||
end
|
||||
|
||||
|
||||
out = out .."\nlist of keys - ".. d .. " digits of base " .. base .. " expansion of numbers : \n" ..
|
||||
dumparr(keylist)
|
||||
|
||||
for step = 1, d do
|
||||
for i =1,n do
|
||||
keys[i] = keylist[i][step];
|
||||
end
|
||||
integer_sort(input, keys, ordering, temp_ordering, base)
|
||||
|
||||
out = out .."\n"..step .. ". pass integer_sort : " .. dumparr(permutate(input,ordering))
|
||||
end
|
||||
|
||||
out = out .. "\nradix sort final result : " .. dumparr(permutate(input,ordering))
|
||||
|
||||
end
|
||||
|
||||
|
||||
--input = {"a","b","c","d","e"}
|
||||
--keys = {5,3,4,1,2}
|
||||
--ordering = {}; temp_ordering = {}; for i = 1, #input do ordering[i]=i end
|
||||
--m=5;
|
||||
|
||||
--integer_sort(input, keys, ordering, temp_ordering,m);
|
||||
--say(string.gsub(_G.dump(ordering),"\n",""))
|
||||
--say(string.gsub(_G.dump(permutate(input,ordering)),"\n",""))
|
||||
out = ""; self.label("")
|
||||
|
||||
input = {23,42,15,8,87};
|
||||
radix_sort(input,5,3) -- d, base
|
||||
self.display_text(out,60,3)
|
||||
end
|
|
@ -0,0 +1,96 @@
|
|||
-- title : serialization
|
||||
-- author : rnd
|
||||
-- description :
|
||||
-- source : https://github.com/ac-minetest/basic_robot/
|
||||
--
|
||||
|
||||
if not itemlist then
|
||||
inv2string = function(name)
|
||||
local inv = _G.minetest.get_player_by_name(name):get_inventory()
|
||||
local list = ""
|
||||
|
||||
for i = 1,32 do
|
||||
local item = inv:get_stack("main", i):to_string()
|
||||
list = list .. item .. ","
|
||||
end
|
||||
return name .. "," .. list
|
||||
end
|
||||
|
||||
string2inv = function(str)
|
||||
local i = string.find(str,",");
|
||||
local name = string.sub(str,1,i-1);
|
||||
local step = 0
|
||||
local invlist = {};
|
||||
while i and step < 33 do
|
||||
step = step +1
|
||||
local i1 = string.find(str,",",i+1)
|
||||
if not i1 then break end
|
||||
invlist[#invlist+1]=string.sub(str,i+1,i1-1)
|
||||
i=i1
|
||||
end
|
||||
return name, invlist
|
||||
end
|
||||
|
||||
array2string = function(array)
|
||||
if _G.type(array) ~= "table" then return array end
|
||||
local ret = "{";
|
||||
for i =1,#array-1 do
|
||||
ret = ret .. array2string(array[i]) .. ","
|
||||
end
|
||||
ret = ret .. array2string(array[#array])
|
||||
return ret .. "}"
|
||||
end
|
||||
|
||||
string2array = function(str)
|
||||
if not string.find(str,"{") then return str end
|
||||
local lvl = 1; local n = string.len(str);
|
||||
local i1,i2,count;
|
||||
count = 0; --1 = {}, 2 = ,
|
||||
local arr = {}; i1 = 2;
|
||||
for i =2,n do
|
||||
local c = string.sub(str,i,i);
|
||||
if c == "{" then
|
||||
lvl = lvl+1
|
||||
elseif c == "}" then
|
||||
lvl = lvl -1
|
||||
elseif c == "," and lvl == 1 then
|
||||
i2 = i;
|
||||
count = count+1
|
||||
arr[count] = string2array(string.sub(str,i1,i2-1)); i1 = i2+1
|
||||
end
|
||||
end
|
||||
if i1< n then count = count+1 ; arr[count] = string2array(string.sub(str,i1,n-1)) end
|
||||
return arr
|
||||
end
|
||||
|
||||
local arr = {{1,{2,4}},{3,4},{0,{2}},-2};
|
||||
--local arr = {1,2,3}
|
||||
self.spam(1)
|
||||
say("original array : ".. string.gsub(_G.dump(arr),"\n","") )
|
||||
local str = array2string(arr);
|
||||
say("array2string : " .. str)
|
||||
local arr1 = string2array(str);
|
||||
say("string2array: " .. string.gsub(_G.dump(arr1),"\n",""))
|
||||
|
||||
player = find_player(2);
|
||||
itemlist = {};
|
||||
if player then
|
||||
local list = inv2string(player[1])
|
||||
local name,invlist = string2inv(list)
|
||||
list = "UNIT TEST inv2string\n"..list .. "\nUNIT TEST string2inv\n".. "name = " .. name .."\n".. string.gsub(_G.dump(invlist),"\n","")
|
||||
form = "size[8,8.5]" ..
|
||||
"textarea[0,0;7.75,7.5;list;list;" .. list .. "]";
|
||||
self.show_form(player[1],form)
|
||||
end
|
||||
end
|
||||
|
||||
sender,fields = self.read_form();
|
||||
if sender then
|
||||
if fields.list then
|
||||
if string.sub(fields.list,1,3) == "DCL" then
|
||||
local sel = tonumber(string.sub(fields.list,5)) or 1
|
||||
say("you selected item " .. itemlist[sel])
|
||||
end
|
||||
end
|
||||
if fields.quit then self.remove() end
|
||||
end
|
|
@ -0,0 +1,148 @@
|
|||
-- title : shop
|
||||
-- author : rnd
|
||||
-- description :
|
||||
-- source : https://github.com/ac-minetest/basic_robot/
|
||||
--
|
||||
|
||||
if not s then
|
||||
s=0;item = 1; price =""; buyer = ""
|
||||
_G.minetest.forceload_block(self.pos(),true)
|
||||
_G.basic_robot.data[self.name()].obj:set_properties({nametag = ""})
|
||||
self.listen(1);self.spam(1)
|
||||
shoplist = {};
|
||||
--scan shops:
|
||||
pos = self.pos(); pos.y=pos.y-5; pos.x=pos.x-6
|
||||
pos1 = {x=pos.x-8,y=pos.y-2,z=pos.z-8};pos2 = {x=pos.x+8,y=pos.y+2,z=pos.z+8};
|
||||
local shoppos = _G.minetest.find_nodes_in_area(pos1, pos2, "shop:shop");
|
||||
--say("scanning... i found " .. #shoppos .. " shops ");
|
||||
count = 0
|
||||
for _,p in pairs(shoppos) do
|
||||
local inv = _G.minetest.get_meta(p):get_inventory()
|
||||
local s = inv:get_list("sell");local b = inv:get_list("buy")
|
||||
local k = s[1]:to_string();
|
||||
local v = b[1]:to_string();
|
||||
if (k and k~="") and (v and v~="") then count = count +1 shoplist[count] = {k,v} end
|
||||
end
|
||||
|
||||
local f_sort = function(a,b) return a[1]<b[1] end;
|
||||
table.sort(shoplist,f_sort)
|
||||
|
||||
itemlist = ""; count = 0;
|
||||
shopinventory = {};
|
||||
for k,v in pairs(shoplist) do
|
||||
if v[1] then
|
||||
count = count +1
|
||||
itemlist = itemlist .. string.format("%-5s%-40s%-32s",k,v[1],v[2]) .. ","
|
||||
end
|
||||
end
|
||||
t = 0; ct = 0; ci = 0
|
||||
say("scanning shops ... completed. Added " .. count .. " items for sale ");
|
||||
end
|
||||
|
||||
|
||||
ct=ct+1
|
||||
if ct%5 == 0 then
|
||||
ct=0
|
||||
ctext = "say #shop to buy";
|
||||
if ci>0 and ci<=count then
|
||||
if shoplist[ci] then
|
||||
iname = shoplist[ci][1];local p=string.find(iname,":"); if p then iname = string.sub(iname,p+1) end
|
||||
sname = shoplist[ci][2];p=string.find(sname,":"); if p then sname = string.sub(sname,p+1) end
|
||||
ctext = "#SHOP ".. ci .."\n" ..iname .. "\nPRICE\n" .. sname
|
||||
end
|
||||
ci=ci+1
|
||||
elseif ci == count+1 then ci=0
|
||||
elseif ci == 0 then ci=1
|
||||
end
|
||||
|
||||
text = "SHOP ROBOT"..os.date("%x") .." " .. os.date("%H:%M:%S").. "\n\n"..ctext
|
||||
self.display_text(text,10,3);
|
||||
end
|
||||
|
||||
speaker,msg = self.listen_msg()
|
||||
if msg then
|
||||
if s == 0 then
|
||||
if string.sub(msg,1,5)=="#shop" then
|
||||
if msg == "#shop" then
|
||||
--say("say #shop command, where command is list OR armor OR item number. Example: #shop list or #shop 1")
|
||||
|
||||
local list = "1,2,3";
|
||||
local form = "size[8,8.5]" ..
|
||||
"label[0.,0.5;ID]"..
|
||||
"label[0.4,0.5;BUY]"..
|
||||
"label[3.,0.5;SELL]" ..
|
||||
"field[7.2,0.25;1.,1;count;count;".. 1 .."]"..
|
||||
"button[5.,-0.05;2.,1;ARMOR;ARMOR]"..
|
||||
"textlist[0,1;7.75,7.5;list;" .. itemlist .. "]";
|
||||
self.show_form(speaker,form)
|
||||
end
|
||||
end
|
||||
elseif s == 1 then
|
||||
if string.sub(msg,1,3)~="buy" then
|
||||
t=t+1; if t>1 then say("timeout. trade cancelled."); s = 0 end
|
||||
else
|
||||
|
||||
|
||||
s=0
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
sender,fields = self.read_form()
|
||||
if sender then
|
||||
local sel = fields.list; --"CHG:3"
|
||||
--say( string.gsub(_G.dump(fields),"\n",""))
|
||||
if sel and string.sub(sel,1,3) == "DCL" then
|
||||
local quantity = tonumber(fields.count) or 1;
|
||||
local select = tonumber(string.sub(sel,5) or "") or 1;
|
||||
local item, price
|
||||
|
||||
if shoplist[select] then
|
||||
item,price = shoplist[select][1],shoplist[select][2];
|
||||
end
|
||||
|
||||
local player = _G.minetest.get_player_by_name(sender);
|
||||
if player and item and price then
|
||||
|
||||
local inv = player:get_inventory();
|
||||
if quantity > 99 then quantity = 99 end
|
||||
if quantity > 1 then
|
||||
local k = 1;
|
||||
local i = string.find(price," ");
|
||||
if i then
|
||||
k = tonumber(string.sub(price,i+1)) or 1
|
||||
price = string.sub(price,1,i-1).. " " .. k*quantity
|
||||
else
|
||||
price = price.. " " .. quantity
|
||||
end
|
||||
|
||||
k=1;i = string.find(item," ");
|
||||
if i then
|
||||
k = tonumber(string.sub(item,i+1)) or 1
|
||||
item = string.sub(item,1,i-1).. " " .. k*quantity
|
||||
else
|
||||
item = item .. " " .. quantity
|
||||
end
|
||||
end
|
||||
|
||||
if inv:contains_item("main", price ) then
|
||||
inv:remove_item("main",price)
|
||||
inv:add_item("main",item)
|
||||
_G.minetest.chat_send_player(sender,"#SHOP ROBOT: " .. item .. " sold to " .. sender .. " for " .. price)
|
||||
else
|
||||
_G.minetest.chat_send_player(sender,"#SHOP ROBOT: you dont have " .. price .. " in inventory ")
|
||||
end
|
||||
end
|
||||
elseif fields.ARMOR then
|
||||
local player = _G.minetest.get_player_by_name(sender);
|
||||
if player then
|
||||
local inv = player:get_inventory();
|
||||
if inv:contains_item("main",_G.ItemStack("default:diamond 30")) then
|
||||
player:set_armor_groups({fleshy = 50})
|
||||
_G.minetest.chat_send_player(sender,"#SHOP ROBOT: you bought 50% damage reduction.")
|
||||
else
|
||||
_G.minetest.chat_send_player(sender,"#SHOP ROBOT: you need 30 diamonds to get armor effect")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,30 @@
|
|||
-- title : simple_layout_gen
|
||||
-- author : rnd
|
||||
-- description :
|
||||
-- source : https://github.com/ac-minetest/basic_robot/
|
||||
--
|
||||
|
||||
if not data then
|
||||
m=50;n=50; minescount = m*n/10;
|
||||
|
||||
t0 = _G.minetest.get_gametime();
|
||||
data = {}; spawnpos = self.spawnpos();
|
||||
for i = 1, minescount do local i = math.random(m); local j = math.random(n); if not data[i] then data[i] = {} end; data[i][j] = 1; end
|
||||
|
||||
get_mine_count = function(i,j)
|
||||
if i<0 or i>m+1 or j<0 or j>n+1 then return 0 end; count = 0
|
||||
for k = -1,1 do for l = -1,1 do
|
||||
if data[i+k] and data[i+k][j+l] == 1 then count = count +1 end
|
||||
end end
|
||||
return count
|
||||
end
|
||||
|
||||
for i = 1,m do for j = 1,n do
|
||||
if get_mine_count(i,j) > 0 or (data[i] and data[i][j] == 1) then
|
||||
_G.minetest.swap_node({x=spawnpos.x+i,y=spawnpos.y,z=spawnpos.z+j}, {name = "basic_robot:buttonFFFFFF"})
|
||||
else
|
||||
_G.minetest.swap_node({x=spawnpos.x+i,y=spawnpos.y,z=spawnpos.z+j}, {name = "default:dirt"})
|
||||
end
|
||||
end end
|
||||
end
|
||||
self.remove()
|
|
@ -0,0 +1,28 @@
|
|||
-- title : simple_parser
|
||||
-- author : rnd
|
||||
-- description :
|
||||
-- source : https://github.com/ac-minetest/basic_robot/
|
||||
--
|
||||
|
||||
if not text then
|
||||
text = "diamond 3;mese 4;gold 2;diamond 1;"
|
||||
function parse(text)
|
||||
ret = {};
|
||||
for a,b in text:gmatch("(%w+) (%w+)%;") do
|
||||
ret[a] = (ret[a] or 0) + (tonumber(b) or 0)
|
||||
end
|
||||
return ret
|
||||
end
|
||||
|
||||
function export(array)
|
||||
ret = "";
|
||||
for k,v in pairs(array) do
|
||||
ret = ret .. (_G.tostring(k) or "") .. " " .. (_G.tostring(v) or "") ..";"
|
||||
end
|
||||
return ret
|
||||
end
|
||||
say("input: " .. text)
|
||||
local arr = parse(text);
|
||||
say("parsed text: " .. string.gsub(_G.dump(arr),"\n",""))
|
||||
say("back to string :" .. export(arr))
|
||||
end
|
|
@ -0,0 +1,70 @@
|
|||
-- title : substring_search
|
||||
-- author : rnd
|
||||
-- description :
|
||||
-- source : https://github.com/ac-minetest/basic_robot/
|
||||
--
|
||||
|
||||
-- Rabin–Karp substring s search in string t
|
||||
-- https://en.wikipedia.org/wiki/Rabin%E2%80%93Karp_algorithm
|
||||
|
||||
|
||||
-- rnd 2017
|
||||
-- ALGORITHM:
|
||||
-- 1.) loop compute hashes of all substrings of t of length |s| using rolling hash idea
|
||||
-- 2.) if some hash matches hash of s check more closely ( waste |s| time here, so this should only occur with probability < O(1)/|s| so expected waste is O(1))
|
||||
|
||||
-- how to do 1) rolling hash: how does hash of string change if you remove first character and add new last character? ... dont need to recompute whole hash!
|
||||
|
||||
-- summary: we end up using O(|t|+|s| + (number of needed hits)*O(1)) time (if you want more than 1 hit..)
|
||||
-- this is big improvement compared to O(|t|*|s|) when doing naive substring search
|
||||
|
||||
-- improvement: we could also precompute all substring hashes of length |s| and then compute hash of some string of same length and
|
||||
-- do quick lookups for that hash ( hash of hash :) )
|
||||
|
||||
if not hash then
|
||||
|
||||
hash = function(s,p)
|
||||
local length = string.len(s);
|
||||
local h = 0 ;
|
||||
for i = 1, length do
|
||||
h=(256*h + string.byte(s,i))%p
|
||||
end
|
||||
return h%p
|
||||
end
|
||||
|
||||
getpower = function(p,k) -- safe computation of power 256^k in mod p arithmetic
|
||||
local r=1; for i = 1,k do r=(256*r) % p end; return r
|
||||
end
|
||||
|
||||
karpin_rabin = function(t,s)
|
||||
local ls = string.len(s);
|
||||
local lt = string.len(t);
|
||||
if lt<ls then return nil end
|
||||
|
||||
local p = 10011;
|
||||
local hs = hash(s,p);
|
||||
local rht = hash(string.sub(t,1,ls),p); -- rolling hash
|
||||
|
||||
if hs == rht then
|
||||
if s == string.sub(t,1,ls) then return 1 end -- match at position 1!
|
||||
end
|
||||
local power256 = getpower(p,ls-1);
|
||||
|
||||
for i = 2,lt-ls+1 do
|
||||
local c1 = string.byte(t,i-1); local c2 = string.byte(t,i+ls-1);
|
||||
rht = (256*(rht - c1*power256)+c2)%p; -- update rolling hash to hash of next substring; this is ok since: a===b then also a*c===b*c
|
||||
if hs == rht then
|
||||
if s == string.sub(t,i,i+ls-1) then return i end -- match at position i!
|
||||
end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
local t = "The Rabin–Karp algorithm is inferior for single pattern searching to Knuth–Morris–Pratt algorithm, Boyer–Moore string search algorithm and other faster single pattern string searching algorithms because of its slow worst case behavior. However, it is an algorithm of choice for multiple pattern search.";
|
||||
local s = "inferior";
|
||||
local chk = karpin_rabin(t,s)
|
||||
if chk then say("found '" .. s .. "' in text pos. " .. chk .." : ..." .. string.sub(t,chk,chk+50) .. "...")
|
||||
else say("could not find '"..s.."' in text")
|
||||
end
|
||||
|
||||
end
|
|
@ -0,0 +1,97 @@
|
|||
-- title : battle_bot_arena
|
||||
-- author : rnd
|
||||
-- description :
|
||||
-- source : https://github.com/ac-minetest/basic_robot/
|
||||
--
|
||||
|
||||
if not s then
|
||||
-- init
|
||||
bots = {[4] = {}, [5] = {}}; -- [type] = {{1,1,10}, {3,2,10}}; -- {x,y,hp}
|
||||
arena = {}; --[x][z] = {type, idx}
|
||||
for i = -10,10 do arena[i] = {} for j=-10,10 do arena[i][j] = {0,0} end end
|
||||
centerpos = self.spawnpos(); centerpos.y = centerpos.y+2
|
||||
TYPE = 4; -- 4,5 defines which bots are on the move/attack
|
||||
DIR = 1
|
||||
s=0
|
||||
t=0
|
||||
-- load user progs
|
||||
_,script1 = book.read(1);_,script2 = book.read(2);
|
||||
prog1, _ = _G.loadstring( script1 ); prog2, _ = _G.loadstring( script2 );
|
||||
|
||||
spawn_bot = function (x,z,type)
|
||||
if arena[x] and arena[x][z] and arena[x][z][1] == 0 then
|
||||
keyboard.set({x=centerpos.x+x,y=centerpos.y,z=centerpos.z+z},type)
|
||||
table.insert(bots[type],{x,z,10})
|
||||
arena[x][z] = {type,#bots[type]}
|
||||
else
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
move_bot = function (i,dx,dz)
|
||||
local bot = bots[TYPE][i];if not bot then return false end
|
||||
if math.abs(dx)>1 or math.abs(dz)>1 then return false end
|
||||
local x1=bot[1]+dx; local z1=bot[2]+dz;
|
||||
if math.abs(x1)>10 or math.abs(z1)>10 then return false end
|
||||
if arena[x1] and arena[x1][z1] and arena[x1][z1][1] == 0 then else return false end
|
||||
|
||||
keyboard.set({x=centerpos.x+bot[1],y=centerpos.y,z=centerpos.z+bot[2]},0);
|
||||
keyboard.set({x=centerpos.x+x1,y=centerpos.y,z=centerpos.z+z1},TYPE);
|
||||
arena[bot[1]][bot[2]] = {0,0}
|
||||
arena[x1][z1] = {TYPE,i}
|
||||
|
||||
bot[1]=x1;bot[2]=z1;
|
||||
end
|
||||
|
||||
attack_bot = function(i,dx,dz)
|
||||
local bot = bots[TYPE][i];if not bot then return false end
|
||||
if math.abs(dx)>1 or math.abs(dz)>1 then return false end
|
||||
local x1=bot[1]+dx; local z1=bot[2]+dz;
|
||||
if math.abs(x1)>10 or math.abs(z1)>10 then return false end
|
||||
if arena[x1] and arena[x1][z1] and arena[x1][z1][1] == 0 then return false end
|
||||
local type = arena[x1][z1][1]; local idx = arena[x1][z1][2];
|
||||
local tbot = bots[type][idx];
|
||||
if not tbot then return false end
|
||||
tbot[3]=tbot[3]-5;
|
||||
if tbot[3]<=0 then
|
||||
keyboard.set({x=centerpos.x+tbot[1],y=centerpos.y,z=centerpos.z+tbot[2]},0);
|
||||
table.remove(bots[type],idx);
|
||||
arena[x1][z1] = {0,0}
|
||||
end
|
||||
end
|
||||
|
||||
read_arena = function(x,z)
|
||||
local data = arena[x][z];
|
||||
if not data then return end
|
||||
return {data[1],data[2]};
|
||||
end
|
||||
|
||||
read_bots = function (type, idx)
|
||||
local data = bots[type][idx];
|
||||
if not data then return end
|
||||
return {data[1],data[2],data[3]}
|
||||
end
|
||||
end
|
||||
|
||||
if t%10 == 0 then
|
||||
spawn_bot(0,-10,4)
|
||||
spawn_bot(0,10,5)
|
||||
end
|
||||
t=t+1
|
||||
self.label(#bots[4] .. " " .. #bots[5])
|
||||
|
||||
-- PROGRAM RULES:
|
||||
-- not allowed to modify api code: TYPE, bots,t,s, spawn_bot, move_bot, attack_bot, read_arena, read_bots
|
||||
-- only allowed to move bot or attack, but not to dig/place
|
||||
|
||||
TYPE = 4+(t%2);
|
||||
DIR = - DIR
|
||||
|
||||
if TYPE == 5 then
|
||||
_G.setfenv(prog1, _G.basic_robot.data[self.name()].sandbox )
|
||||
_,err = pcall(prog1)
|
||||
else
|
||||
_G.setfenv(prog2, _G.basic_robot.data[self.name()].sandbox )
|
||||
_,err = pcall(prog2)
|
||||
end
|
||||
if err then say(err) self.remove() end
|
|
@ -0,0 +1,156 @@
|
|||
-- title : battle_minesweeper
|
||||
-- author : rnd
|
||||
-- description :
|
||||
-- source : https://github.com/ac-minetest/basic_robot/
|
||||
--
|
||||
|
||||
if not data then
|
||||
m=10;n=10;
|
||||
players = {};
|
||||
paused = true
|
||||
|
||||
turn = 2;
|
||||
t = 0;
|
||||
SIGNUP = 0; GAME = 1; INTERMISSION = 2
|
||||
state = SIGNUP
|
||||
|
||||
t0 = _G.minetest.get_gametime();spawnpos = self.spawnpos() -- place mines
|
||||
data = {};
|
||||
|
||||
init_game = function()
|
||||
data = {}; minescount = 32
|
||||
for i = 1, minescount do local i = math.random(m); local j = math.random(n); if not data[i] then data[i] = {} end; data[i][j] = 1; end
|
||||
if not data[1] then data[1] = {} end if not data[2] then data[2] = {} end -- create 2x2 safe area
|
||||
data[1][1] = 0;data[1][2] = 0;data[2][1] = 0;data[2][2] = 0;
|
||||
|
||||
minescount = 0;
|
||||
for i = 1,m do for j = 1,n do -- render game
|
||||
if data[i] and data[i][j] == 1 then minescount = minescount + 1 end
|
||||
if keyboard.read({x=spawnpos.x+i,y=spawnpos.y,z=spawnpos.z+j})~="basic_robot:button808080" then
|
||||
keyboard.set({x=spawnpos.x+i,y=spawnpos.y,z=spawnpos.z+j},2)
|
||||
end
|
||||
end end
|
||||
keyboard.set({x=spawnpos.x+1,y=spawnpos.y,z=spawnpos.z+1},4) -- safe start spot
|
||||
end
|
||||
|
||||
get_mine_count = function(i,j)
|
||||
if i<0 or i>m+1 or j<0 or j>n+1 then return 0 end; count = 0
|
||||
for k = -1,1 do for l = -1,1 do
|
||||
if data[i+k] and data[i+k][j+l] == 1 then count = count +1 end
|
||||
end end
|
||||
return count
|
||||
end
|
||||
chk_mines = function()
|
||||
local count = minescount;
|
||||
for i=1,m do for j=1,n do
|
||||
if keyboard.read({x=spawnpos.x+i,y=spawnpos.y,z=spawnpos.z+j})=="basic_robot:buttonFF8080" and data[i] and data[i][j]==1 then
|
||||
count=count-1
|
||||
end
|
||||
end end
|
||||
return count
|
||||
end
|
||||
|
||||
greeting = function()
|
||||
_G.minetest.chat_send_all(colorize("red","#BATTLE MINESWEEPER : two player battle in minesweeper. say join to play.\nRules: 1. each player has 5 second turn to make a move 2. if you dont make move you lose\n3. if you make move in other player turn you lose. 4. if you hit bomb or mark bomb falsely you lose"))
|
||||
end
|
||||
|
||||
player_lost = function ()
|
||||
for i=1,#players do
|
||||
local player = _G.minetest.get_player_by_name(players[i]);
|
||||
if player then player:setpos({x=spawnpos.x,y=spawnpos.y+10,z=spawnpos.z}) end
|
||||
end
|
||||
state = INTERMISSION; t = 0
|
||||
end
|
||||
|
||||
function change_turn()
|
||||
if turn == 1 then
|
||||
_G.minetest.sound_play("default_break_glass",{pos=spawnpos, max_hear_distance = 100})
|
||||
else
|
||||
_G.minetest.sound_play("note_a",{pos=spawnpos, max_hear_distance = 100})
|
||||
end
|
||||
|
||||
if paused == false then
|
||||
say(players[turn] .. " lost : didn't make a move");
|
||||
player_lost()
|
||||
else
|
||||
if turn == 1 then turn = 2 else turn = 1 end
|
||||
self.label("turn " .. turn .. " " .. players[turn])
|
||||
t=0
|
||||
paused = false
|
||||
end
|
||||
end
|
||||
|
||||
init_game()
|
||||
greeting()
|
||||
self.listen(1)
|
||||
end
|
||||
|
||||
if state == SIGNUP then
|
||||
speaker,msg = self.listen_msg()
|
||||
if speaker then
|
||||
if msg == "join" then
|
||||
players[#players+1] = speaker;
|
||||
local plist = ""; for i=1,#players do plist = plist .. players[i] .. ", " end
|
||||
_G.minetest.chat_send_all("BATTLE MINESWEEPER, current players : " .. plist)
|
||||
|
||||
if #players >= 2 then
|
||||
state = GAME
|
||||
change_turn();
|
||||
keyboard.get(); t=0;
|
||||
for i = 1, #players do
|
||||
local player = _G.minetest.get_player_by_name(players[i]);
|
||||
if player then player:setpos({x=spawnpos.x,y=spawnpos.y+1,z=spawnpos.z}) end
|
||||
end
|
||||
_G.minetest.chat_send_all(colorize("red","BATTLE MINESWEEPER " .. m .. "x" ..n .. " with " .. minescount .. " mines.\n" .. players[turn] .. " its your move!"))
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
elseif state == GAME then
|
||||
|
||||
t = t + 1;
|
||||
if t>5 then -- change of turn
|
||||
change_turn()
|
||||
end
|
||||
|
||||
event = keyboard.get();
|
||||
if event and event.type == 2 and not paused then
|
||||
if event.puncher == players[turn] then
|
||||
local x = event.x - spawnpos.x;local y = event.y - spawnpos.y;local z = event.z - spawnpos.z;
|
||||
if x<1 or x>m or z<1 or z>n then
|
||||
else
|
||||
local ppos = player.getpos(event.puncher)
|
||||
if ppos and math.abs(ppos.x-event.x)<0.5 and math.abs(ppos.z-event.z)<0.5 then -- just mark mine
|
||||
if data[x] and data[x][z] == 1 then
|
||||
if keyboard.read({x=event.x,y=event.y,z=event.z})~="basic_robot:button808080" then
|
||||
keyboard.set({x=event.x,y=event.y,z=event.z},2)
|
||||
else
|
||||
keyboard.set({x=event.x,y=event.y,z=event.z},3)
|
||||
end
|
||||
else
|
||||
say(event.puncher .. " lost : marked a bomb where it was none! ");
|
||||
player_lost()
|
||||
end
|
||||
else
|
||||
if data[x] and data[x][z]==1 then
|
||||
_G.minetest.sound_play("tnt_boom",{pos=spawnpos, max_hear_distance = 100})
|
||||
keyboard.set({x=spawnpos.x+x,y=spawnpos.y,z=spawnpos.z+z},3)
|
||||
say(event.puncher .. " lost : punched a bomb! ");
|
||||
player_lost()
|
||||
else
|
||||
local count = get_mine_count(x,z);
|
||||
if count == 0 then keyboard.set({x=spawnpos.x+x,y=spawnpos.y,z=spawnpos.z+z},4)
|
||||
else keyboard.set({x=spawnpos.x+x,y=spawnpos.y,z=spawnpos.z+z},7+count) end
|
||||
end
|
||||
end
|
||||
end
|
||||
paused = true
|
||||
else
|
||||
say(event.puncher .. " lost : played out of his/her turn"); player_lost()
|
||||
end
|
||||
end
|
||||
|
||||
elseif state == INTERMISSION then
|
||||
t=t+1; if t> 15 then state = SIGNUP;players = {}; paused = true; init_game(); greeting() end
|
||||
end
|
|
@ -0,0 +1,201 @@
|
|||
-- title : blackbox_game
|
||||
-- author : rnd
|
||||
-- description :
|
||||
-- source : https://github.com/ac-minetest/basic_robot/
|
||||
--
|
||||
|
||||
--black box by rnd, 03/18/2017
|
||||
--https://en.wikipedia.org/wiki/Black_Box_(game)
|
||||
|
||||
if not data then
|
||||
m=8;n=8;turn = 0;
|
||||
attempts = 1;
|
||||
|
||||
self.spam(1);t0 = _G.minetest.get_gametime();
|
||||
spawnpos = self.spawnpos()
|
||||
data = {};
|
||||
for i = 1,m do data[i]={}; for j = 1,n do data[i][j]=0 end end
|
||||
|
||||
for i=1,4 do -- put in 4 atoms randomly
|
||||
data[math.random(m)][math.random(n)] = 1
|
||||
end
|
||||
|
||||
render_board = function(mode) -- mode 0 : render without solution, 1: render solution
|
||||
for i = 1,m do for j = 1,n do -- render game
|
||||
if mode == 0 or data[i][j] == 0 then
|
||||
if keyboard.read({x=spawnpos.x+i,y=spawnpos.y,z=spawnpos.z+j})~="basic_robot:button808080" then
|
||||
keyboard.set({x=spawnpos.x+i,y=spawnpos.y,z=spawnpos.z+j},2)
|
||||
end
|
||||
else
|
||||
keyboard.set({x=spawnpos.x+i,y=spawnpos.y,z=spawnpos.z+j},3)
|
||||
end
|
||||
end end
|
||||
end
|
||||
|
||||
get_dirl = function(dir)
|
||||
local dirl; -- direction left
|
||||
if dir[1] > 0.5 then dirl = {0,-1}
|
||||
elseif dir[1] < -0.5 then dirl = {0,1}
|
||||
elseif dir[2] > 0.5 then dirl = {-1,0}
|
||||
elseif dir[2] < -0.5 then dirl = {1,0}
|
||||
end
|
||||
return dirl
|
||||
end
|
||||
|
||||
read_pos = function(x,z)
|
||||
if x<1 or x>m or z<1 or z>n then return nil end
|
||||
return data[x][z]
|
||||
end
|
||||
|
||||
newdir = function(x,z,dir) -- where will ray go next
|
||||
local retdir = {dir[1],dir[2]};
|
||||
local xf = x+dir[1]; local zf = z+dir[2] -- forward
|
||||
local dirl = get_dirl(dir)
|
||||
|
||||
local nodef = read_pos(xf,zf)
|
||||
local nodel = read_pos(xf + dirl[1],zf + dirl[2])
|
||||
local noder = read_pos(xf - dirl[1],zf - dirl[2])
|
||||
if nodef == 1 then
|
||||
retdir = {0,0} -- ray hit something
|
||||
elseif nodel == 1 and noder ~= 1 then
|
||||
retdir = {-dirl[1],-dirl[2]}
|
||||
elseif nodel ~= 1 and noder == 1 then
|
||||
retdir = {dirl[1],dirl[2]}
|
||||
elseif nodel == 1 and noder == 1 then
|
||||
retdir = {-dir[1],-dir[2]}
|
||||
end
|
||||
return retdir
|
||||
end
|
||||
|
||||
shootray = function(x,z,dir)
|
||||
--say("ray starts " .. x .. " " .. z .. " dir " .. dir[1] .. " " .. dir[2])
|
||||
local xp = x; local zp = z;
|
||||
local dirp = {dir[1],dir[2]};
|
||||
local maxstep = m*n;
|
||||
|
||||
for i = 1,maxstep do
|
||||
dirp = newdir(xp,zp,dirp);
|
||||
if dirp[1]==0 and dirp[2]==0 then return -i end -- hit
|
||||
xp=xp+dirp[1];zp=zp+dirp[2];
|
||||
if xp<1 or xp>m or zp<1 or zp>n then return i,{xp,zp} end -- out
|
||||
end
|
||||
return 0 -- hit
|
||||
end
|
||||
|
||||
count = 0; -- how many letters were used up
|
||||
border_start_ray = function(x,z)
|
||||
local rdir
|
||||
if x==0 then rdir = {1,0}
|
||||
elseif x == m+1 then rdir = {-1,0}
|
||||
elseif z == 0 then rdir = {0,1}
|
||||
elseif z == n+1 then rdir = {0,-1}
|
||||
end
|
||||
if rdir then
|
||||
local result,out = shootray(x,z,rdir);
|
||||
if result >= 0 then
|
||||
|
||||
if out then
|
||||
if out[1]==x and out[2]==z then -- got back where it originated, reflection
|
||||
keyboard.set({x=spawnpos.x+out[1],y=spawnpos.y,z=spawnpos.z+out[2]},1);
|
||||
else
|
||||
if result<=1 then
|
||||
keyboard.set({x=spawnpos.x+x,y=spawnpos.y,z=spawnpos.z+z},6); -- immediate bounce off
|
||||
else
|
||||
local nodename = "default:obsidian_letter_"..string.char(97+count) .. "u";
|
||||
_G.minetest.set_node(
|
||||
{x=spawnpos.x+out[1],y=spawnpos.y+1,z=spawnpos.z+out[2]},
|
||||
{name = nodename, param2 = 1})
|
||||
_G.minetest.set_node(
|
||||
{x=spawnpos.x+x,y=spawnpos.y+1,z=spawnpos.z+z},
|
||||
{name = nodename, param2 = 1})
|
||||
count = count + 1;
|
||||
keyboard.set({x=spawnpos.x+x,y=spawnpos.y,z=spawnpos.z+z},4);
|
||||
keyboard.set({x=spawnpos.x+out[1],y=spawnpos.y,z=spawnpos.z+out[2]},4);
|
||||
end
|
||||
end
|
||||
end
|
||||
elseif result<0 then
|
||||
keyboard.set({x=spawnpos.x+x,y=spawnpos.y,z=spawnpos.z+z},3); -- hit
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- initial border loop and marking
|
||||
|
||||
--render blue border
|
||||
for i = 1,m do keyboard.set({x=spawnpos.x+i,y=spawnpos.y,z=spawnpos.z+0},5) keyboard.set({x=spawnpos.x+i,y=spawnpos.y,z=spawnpos.z+n+1},5) end
|
||||
for j = 1,n do keyboard.set({x=spawnpos.x+0,y=spawnpos.y,z=spawnpos.z+j},5) keyboard.set({x=spawnpos.x+m+1,y=spawnpos.y,z=spawnpos.z+j},5) end
|
||||
|
||||
for i = 1,m do keyboard.set({x=spawnpos.x+i,y=spawnpos.y+1,z=spawnpos.z+0},0) keyboard.set({x=spawnpos.x+i,y=spawnpos.y+1,z=spawnpos.z+n+1},0) end
|
||||
for j = 1,n do keyboard.set({x=spawnpos.x+0,y=spawnpos.y+1,z=spawnpos.z+j},0) keyboard.set({x=spawnpos.x+m+1,y=spawnpos.y+1,z=spawnpos.z+j},0) end
|
||||
|
||||
|
||||
z=0 -- bottom
|
||||
for x = 1,m do
|
||||
if keyboard.read({x=spawnpos.x+x,y=spawnpos.y,z=spawnpos.z+z}) == "basic_robot:button8080FF" then
|
||||
border_start_ray(x,z)
|
||||
end
|
||||
end
|
||||
|
||||
x=m+1 -- right
|
||||
for z = 1,n do
|
||||
if keyboard.read({x=spawnpos.x+x,y=spawnpos.y,z=spawnpos.z+z}) == "basic_robot:button8080FF" then
|
||||
border_start_ray(x,z)
|
||||
end
|
||||
end
|
||||
|
||||
z=n+1 -- top
|
||||
for x = m,1,-1 do
|
||||
if keyboard.read({x=spawnpos.x+x,y=spawnpos.y,z=spawnpos.z+z}) == "basic_robot:button8080FF" then
|
||||
border_start_ray(x,z)
|
||||
end
|
||||
end
|
||||
|
||||
x=0 -- left
|
||||
for z = n,1,-1 do
|
||||
if keyboard.read({x=spawnpos.x+x,y=spawnpos.y,z=spawnpos.z+z}) == "basic_robot:button8080FF" then
|
||||
border_start_ray(x,z)
|
||||
end
|
||||
end
|
||||
|
||||
check_solution = function()
|
||||
for i = 1,m do
|
||||
for j = 1,n do
|
||||
if keyboard.read({x=spawnpos.x+i,y=spawnpos.y,z=spawnpos.z+j}) == "basic_robot:buttonFF8080" then -- red
|
||||
if data[i][j]~=1 then return false end
|
||||
else
|
||||
if data[i][j]~=0 then return false end
|
||||
end
|
||||
end
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
--render board
|
||||
render_board(0)
|
||||
keyboard.set({x=spawnpos.x,y=spawnpos.y,z=spawnpos.z-1},5)
|
||||
|
||||
end
|
||||
|
||||
event = keyboard.get();
|
||||
if event then
|
||||
local x = event.x - spawnpos.x;local y = event.y - spawnpos.y;local z = event.z - spawnpos.z;
|
||||
if x<1 or x>m or z<1 or z>n then
|
||||
if event.type == 5 then
|
||||
if check_solution() then
|
||||
say("#BLACKBOX : CONGRATULATIONS! " .. event.puncher .. " found all atoms after " .. attempts .. " tries."); self.remove()
|
||||
else
|
||||
say("#BLACKBOX : " .. event.puncher .. " failed to detect atoms after " .. attempts .. " attempts.")
|
||||
attempts = attempts+1
|
||||
end
|
||||
end
|
||||
else -- interior punch
|
||||
nodetype = 2;
|
||||
if keyboard.read({x=spawnpos.x+x,y=spawnpos.y,z=spawnpos.z+z}) == "basic_robot:button808080" then
|
||||
nodetype = 3
|
||||
end
|
||||
keyboard.set({x=spawnpos.x+x,y=spawnpos.y,z=spawnpos.z+z},nodetype);
|
||||
end
|
||||
|
||||
end
|
||||
::END::
|
|
@ -0,0 +1,114 @@
|
|||
-- title : hash_table_implementation
|
||||
-- author : rnd
|
||||
-- description :
|
||||
-- source : https://github.com/ac-minetest/basic_robot/
|
||||
--
|
||||
|
||||
if not get_hash then
|
||||
|
||||
get_hash = function(s,p)
|
||||
if not s then return end
|
||||
local h = 0; local n = string.len(s);local m = 4; -- put 4 characters together
|
||||
local r = 0;local i = 0;
|
||||
while i<n do
|
||||
i=i+1;r = 256*r+ string.byte(s,i);
|
||||
if i%m == 0 then h=h+(r%p) r=0 end
|
||||
end
|
||||
if i%m~=0 then h=h+(r%p) end
|
||||
return h%p
|
||||
end
|
||||
|
||||
|
||||
hashdb = {}; --array with entries: [hash] = list of possible hits
|
||||
|
||||
insert = function(key,value)
|
||||
local hash = get_hash(key,10011);
|
||||
local data = hashdb[hash]; if data == nil then hashdb[hash] = {}; data = hashdb[hash] end
|
||||
data[#data+1]={key,value};
|
||||
return hash
|
||||
end
|
||||
|
||||
lookup = function(key)
|
||||
local hash = get_hash(key,10011);
|
||||
if not hash then return end
|
||||
local data = hashdb[hash];
|
||||
if not data then return nil end
|
||||
for i = 1,#data do
|
||||
if data[i][1]==key then return data[i][2] end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
analyse = function()
|
||||
local count = 0; local maxlen = 0; local maxidx = 1; local n = #hashdb;
|
||||
for i = 1, 10000 do
|
||||
local data = hashdb[i];
|
||||
if data then
|
||||
local length = #data;
|
||||
if length > maxlen then maxlen = length; maxidx = i end
|
||||
count = count + 1
|
||||
end
|
||||
end
|
||||
|
||||
if maxlen>0 then
|
||||
local data = hashdb[maxidx];
|
||||
say("number of used hash entries is " .. count .. ", average " .. (step/count) .. " entries per hash, "..
|
||||
" max length of list is " .. maxlen .. " at hash ".. maxidx )--.. " : " ..
|
||||
--string.gsub(_G.dump(data),"\n","") )
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
-- LOAD DICTIONARY WORDS into hashtable
|
||||
|
||||
lang = "german"
|
||||
|
||||
fname = "F:\\games\\rpg\\minetest-0415server\\mods\\basic_translate\\"..lang;
|
||||
local f = _G.assert(_G.io.open(fname, "r"));local dicts = f:read("*all");f:close()
|
||||
|
||||
step = 0; maxwords = 10000;
|
||||
i=0
|
||||
dict = {}; -- for comparison
|
||||
|
||||
while(step<maxwords) do
|
||||
|
||||
step=step+1
|
||||
i1 = string.find(dicts,"\t",i+1)
|
||||
i2 = string.find(dicts,"\n",i+1)
|
||||
|
||||
if not i2 then break end
|
||||
|
||||
local word = string.sub(dicts, i+1,i1-1);
|
||||
local tword = string.sub(dicts, i1+1,i2-1);
|
||||
insert(word, tword) -- load into hashtable
|
||||
dict[word]=tword
|
||||
i=i2
|
||||
end
|
||||
|
||||
self.listen(1)
|
||||
self.spam(1)
|
||||
say(step .. " words loaded")
|
||||
end
|
||||
|
||||
-- handle chat event
|
||||
speaker,msg = self.listen_msg()
|
||||
|
||||
if msg then
|
||||
if msg == "?*" then
|
||||
local n = 1000000; local msg = "hello"
|
||||
local ret = "";
|
||||
local t1 = os.clock();
|
||||
for i = 1, n do ret = lookup(msg) end; t1 = os.clock()-t1;
|
||||
local t2 = os.clock();
|
||||
for i = 1, n do ret = dict[msg] end
|
||||
t2 = os.clock()-t2;
|
||||
say(t1 .. " " .. t2)
|
||||
|
||||
elseif msg == "??" then
|
||||
analyse()
|
||||
elseif string.sub(msg,1,1)=="?" then
|
||||
msg = string.sub(msg,2);
|
||||
local ret = lookup(msg);
|
||||
if ret then say("found entry for " .. msg .. " : " .. ret) else say("entry not found") end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,44 @@
|
|||
-- title : messenger
|
||||
-- author : rnd
|
||||
-- description :
|
||||
-- source : https://github.com/ac-minetest/basic_robot/
|
||||
--
|
||||
|
||||
if not s then
|
||||
s=0
|
||||
msgsize = 2500;
|
||||
_,text = book.read(1);text = text or "";
|
||||
write_msg = function(sender,msg)
|
||||
|
||||
local newsize = string.len(text)+string.len(msg);
|
||||
if newsize>msgsize then return "messages space exceeded" end
|
||||
text = text .. "\n"..os.date() .. " " .. sender .. ": " .. msg;
|
||||
book.write(1,"messages",text)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
--textarea[X,Y;W,H;name;label;default]
|
||||
--button[X,Y;W,H;name;label]
|
||||
if s == 0 then
|
||||
players = find_player(4);
|
||||
if players and players[1] then
|
||||
s=1
|
||||
local form = "size[8,4.5]" ..
|
||||
"textarea[0,0;9,4.5;msg;MESSAGE FOR ADMIN;]"..
|
||||
"button_exit[-0.5,4.15;2,1;send;send]"
|
||||
self.show_form(players[1],form)
|
||||
end
|
||||
elseif s==1 then
|
||||
sender,fields = self.read_form();
|
||||
if sender then
|
||||
if fields.send then
|
||||
msg = fields.msg;
|
||||
if msg and msg~="" then
|
||||
write_msg(sender,msg);activate.up(1)
|
||||
_G.minetest.chat_send_player(sender,"#mailbot: message has been stored")
|
||||
end
|
||||
end
|
||||
self.remove()
|
||||
end
|
||||
end
|
|
@ -0,0 +1,78 @@
|
|||
-- title : minesweeper_game
|
||||
-- author : rnd
|
||||
-- description :
|
||||
-- source : https://github.com/ac-minetest/basic_robot/
|
||||
--
|
||||
|
||||
if not data then
|
||||
m=10;n=10; minescount = 32;
|
||||
|
||||
t0 = _G.minetest.get_gametime();
|
||||
data = {}; spawnpos = self.spawnpos() -- place mines
|
||||
for i = 1, minescount do local i = math.random(m); local j = math.random(n); if not data[i] then data[i] = {} end; data[i][j] = 1; end
|
||||
if not data[1] then data[1] = {} end if not data[2] then data[2] = {} end -- create 2x2 safe area
|
||||
data[1][1] = 0;data[1][2] = 0;data[2][1] = 0;data[2][2] = 0;
|
||||
|
||||
minescount = 0;
|
||||
for i = 1,m do for j = 1,n do -- render game
|
||||
if data[i] and data[i][j] == 1 then minescount = minescount + 1 end
|
||||
if keyboard.read({x=spawnpos.x+i,y=spawnpos.y,z=spawnpos.z+j})~="basic_robot:button808080" then
|
||||
keyboard.set({x=spawnpos.x+i,y=spawnpos.y,z=spawnpos.z+j},2)
|
||||
end
|
||||
end end
|
||||
keyboard.set({x=spawnpos.x+1,y=spawnpos.y,z=spawnpos.z+1},4) -- safe start spot
|
||||
get_mine_count = function(i,j)
|
||||
if i<0 or i>m+1 or j<0 or j>n+1 then return 0 end; count = 0
|
||||
for k = -1,1 do for l = -1,1 do
|
||||
if data[i+k] and data[i+k][j+l] == 1 then count = count +1 end
|
||||
end end
|
||||
return count
|
||||
end
|
||||
chk_mines = function()
|
||||
local count = minescount;
|
||||
for i=1,m do for j=1,n do
|
||||
if keyboard.read({x=spawnpos.x+i,y=spawnpos.y,z=spawnpos.z+j})=="basic_robot:buttonFF8080" and data[i] and data[i][j]==1 then
|
||||
count=count-1
|
||||
end
|
||||
end end
|
||||
return count
|
||||
end
|
||||
say("minesweeper " .. m .. "x" ..n .. " with " .. minescount .. " mines ")
|
||||
self.label("find all hidden mines! mark mine by standing on top of block and punch,\notherwise it will uncover the block (and possibly explode).")
|
||||
|
||||
end
|
||||
|
||||
event = keyboard.get();
|
||||
if event then
|
||||
local x = event.x - spawnpos.x;local y = event.y - spawnpos.y;local z = event.z - spawnpos.z;
|
||||
if x<1 or x>m or z<1 or z>n then
|
||||
if x == 0 and z == 1 then
|
||||
local count = chk_mines();
|
||||
if count == 0 then
|
||||
t0 = _G.minetest.get_gametime() - t0;
|
||||
say("congratulations! " .. event.puncher .. " discovered all mines in " .. t0 .. " s")
|
||||
_G.minetest.add_item({x=spawnpos.x,y=spawnpos.y+1,z=spawnpos.z},_G.itemstack("default:diamond 5")) -- diamond reward
|
||||
else
|
||||
say("FAIL! " .. count .. " mines remaining ")
|
||||
end
|
||||
self.remove()
|
||||
end
|
||||
elseif event.type == 2 then
|
||||
local ppos = player.getpos(event.puncher)
|
||||
if ppos and math.abs(ppos.x-event.x)<0.5 and math.abs(ppos.z-event.z)<0.5 then -- just mark mine
|
||||
if keyboard.read({x=event.x,y=event.y,z=event.z})~="basic_robot:button808080" then
|
||||
keyboard.set({x=event.x,y=event.y,z=event.z},2)
|
||||
else
|
||||
keyboard.set({x=event.x,y=event.y,z=event.z},3)
|
||||
end
|
||||
else
|
||||
if data[x] and data[x][z]==1 then
|
||||
say("boom! "..event.puncher .. " is dead ");keyboard.set({x=spawnpos.x+x,y=spawnpos.y,z=spawnpos.z+z},3);self.remove()
|
||||
else
|
||||
local count = get_mine_count(x,z);
|
||||
if count == 0 then keyboard.set({x=spawnpos.x+x,y=spawnpos.y,z=spawnpos.z+z},4)
|
||||
else keyboard.set({x=spawnpos.x+x,y=spawnpos.y,z=spawnpos.z+z},7+count) end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,78 @@
|
|||
-- title : minesweeper
|
||||
-- author : rnd
|
||||
-- description :
|
||||
-- source : https://github.com/ac-minetest/basic_robot/
|
||||
--
|
||||
|
||||
if not data then
|
||||
m=10;n=10; minescount = 32;
|
||||
|
||||
t0 = _G.minetest.get_gametime();
|
||||
data = {}; spawnpos = self.spawnpos() -- place mines
|
||||
for i = 1, minescount do local i = math.random(m); local j = math.random(n); if not data[i] then data[i] = {} end; data[i][j] = 1; end
|
||||
if not data[1] then data[1] = {} end if not data[2] then data[2] = {} end -- create 2x2 safe area
|
||||
data[1][1] = 0;data[1][2] = 0;data[2][1] = 0;data[2][2] = 0;
|
||||
|
||||
minescount = 0;
|
||||
for i = 1,m do for j = 1,n do -- render game
|
||||
if data[i] and data[i][j] == 1 then minescount = minescount + 1 end
|
||||
if keyboard.read({x=spawnpos.x+i,y=spawnpos.y,z=spawnpos.z+j})~="basic_robot:button808080" then
|
||||
keyboard.set({x=spawnpos.x+i,y=spawnpos.y,z=spawnpos.z+j},2)
|
||||
end
|
||||
end end
|
||||
keyboard.set({x=spawnpos.x+1,y=spawnpos.y,z=spawnpos.z+1},4) -- safe start spot
|
||||
get_mine_count = function(i,j)
|
||||
if i<0 or i>m+1 or j<0 or j>n+1 then return 0 end; count = 0
|
||||
for k = -1,1 do for l = -1,1 do
|
||||
if data[i+k] and data[i+k][j+l] == 1 then count = count +1 end
|
||||
end end
|
||||
return count
|
||||
end
|
||||
chk_mines = function()
|
||||
local count = minescount;
|
||||
for i=1,m do for j=1,n do
|
||||
if keyboard.read({x=spawnpos.x+i,y=spawnpos.y,z=spawnpos.z+j})=="basic_robot:buttonFF8080" and data[i] and data[i][j]==1 then
|
||||
count=count-1
|
||||
end
|
||||
end end
|
||||
return count
|
||||
end
|
||||
say("minesweeper " .. m .. "x" ..n .. " with " .. minescount .. " mines ")
|
||||
self.label("find all hidden mines! mark mine by standing on top of block and punch,\notherwise it will uncover the block (and possibly explode).")
|
||||
|
||||
end
|
||||
|
||||
event = keyboard.get();
|
||||
if event then
|
||||
local x = event.x - spawnpos.x;local y = event.y - spawnpos.y;local z = event.z - spawnpos.z;
|
||||
if x<1 or x>m or z<1 or z>n then
|
||||
if x == 0 and z == 1 then
|
||||
local count = chk_mines();
|
||||
if count == 0 then
|
||||
t0 = _G.minetest.get_gametime() - t0;
|
||||
say("congratulations! " .. event.puncher .. " discovered all mines in " .. t0 .. " s")
|
||||
_G.minetest.add_item({x=spawnpos.x,y=spawnpos.y+1,z=spawnpos.z},_G.itemstack("default:diamond 5")) -- diamond reward
|
||||
else
|
||||
say("FAIL! " .. count .. " mines remaining ")
|
||||
end
|
||||
self.remove()
|
||||
end
|
||||
elseif event.type == 2 then
|
||||
local ppos = player.getpos(event.puncher)
|
||||
if ppos and math.abs(ppos.x-event.x)<0.5 and math.abs(ppos.z-event.z)<0.5 then -- just mark mine
|
||||
if keyboard.read({x=event.x,y=event.y,z=event.z})~="basic_robot:button808080" then
|
||||
keyboard.set({x=event.x,y=event.y,z=event.z},2)
|
||||
else
|
||||
keyboard.set({x=event.x,y=event.y,z=event.z},3)
|
||||
end
|
||||
else
|
||||
if data[x] and data[x][z]==1 then
|
||||
say("boom! "..event.puncher .. " is dead ");keyboard.set({x=spawnpos.x+x,y=spawnpos.y,z=spawnpos.z+z},3);self.remove()
|
||||
else
|
||||
local count = get_mine_count(x,z);
|
||||
if count == 0 then keyboard.set({x=spawnpos.x+x,y=spawnpos.y,z=spawnpos.z+z},4)
|
||||
else keyboard.set({x=spawnpos.x+x,y=spawnpos.y,z=spawnpos.z+z},7+count) end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,190 @@
|
|||
-- title : multiplication
|
||||
-- author : rnd
|
||||
-- description :
|
||||
-- source : https://github.com/ac-minetest/basic_robot/
|
||||
--
|
||||
|
||||
if not number then
|
||||
number = {};
|
||||
number.base = 10; -- what base: 2-10
|
||||
number.data = {};
|
||||
number.size = -1;
|
||||
|
||||
function number:tostring()
|
||||
local ret = ""
|
||||
--say("tostring size " .. self.size)
|
||||
for i = self.size,1,-1 do ret = ret .. (self.data[i] or "X").."" end
|
||||
return ret
|
||||
end
|
||||
|
||||
function number:new(data)
|
||||
local o = {};_G.setmetatable(o, self); o.data = {};
|
||||
for i = 1,#data do o.data[i] = data[i] end -- do copy otherwise it just saves reference
|
||||
o.size = #data;
|
||||
self.__index = self; return o
|
||||
end
|
||||
|
||||
number.add = function (lhs, rhs,res)
|
||||
local n1 = lhs.size;local n2 = rhs.size;local n = math.max(n1,n2);
|
||||
local carry=0, sum; local base = lhs.base;
|
||||
local out = false;
|
||||
if not res then res = number:new({}) out = true end
|
||||
local data = res.data
|
||||
for i = 1,n do
|
||||
sum = (lhs.data[i] or 0)+(rhs.data[i] or 0)+carry;
|
||||
if carry>0 then carry = 0 end
|
||||
if sum>=base then data[i]=sum-base; carry = 1 else data[i] = sum end
|
||||
end
|
||||
if carry>0 then data[n+1]=1 res.size = n+1 else res.size = n end
|
||||
if out then return res end
|
||||
end
|
||||
|
||||
number.__add = add;
|
||||
|
||||
function number:set(m)
|
||||
local data = self.data;
|
||||
local mdata = m.data;
|
||||
for i=1,#mdata do
|
||||
data[i]=mdata[i];
|
||||
end
|
||||
self.size = m.size;
|
||||
end
|
||||
|
||||
-- 'slow' long multiply
|
||||
number.multiply = function (lhs, rhs, res)
|
||||
local n1 = lhs.size;local n2 = rhs.size;local n = n1+n2;
|
||||
--say("multiply sizes " .. n1 .. "," .. n2)
|
||||
local out = false;
|
||||
if not res then res = number:new({}); out = true end;
|
||||
res.size = n1+n2-1;
|
||||
res.data = {} -- if data not cleared it will interfere with result!
|
||||
local data = res.data;
|
||||
local c,prod,carry = 0; local base = lhs.base;
|
||||
for i=1,n1 do
|
||||
carry = 0;
|
||||
c = lhs.data[i] or 0;
|
||||
for j = 1,n2 do -- multiply with i-th digit and add to result
|
||||
prod = (data[i+j-1] or 0)+c*(rhs.data[j] or 0)+carry;
|
||||
carry = math.floor(prod / base);
|
||||
prod = prod % base;
|
||||
data[i+j-1] = (prod)%base;
|
||||
end
|
||||
if carry>0 then data[i+n2] = (data[i+n2] or 0)+ carry ;if res.size<i+n2 then res.size = i+n2 end end
|
||||
end
|
||||
if out then return res end
|
||||
end
|
||||
|
||||
--TO DO
|
||||
number.karatsuba_multiply = function(lhs,rhs,res)
|
||||
local n1 = lhs.size;local n2 = rhs.size;
|
||||
local n0 = math.max(n1,n2);
|
||||
if n0< 2 then -- normal multiply for "normal" sized numbers
|
||||
number.multiply(lhs, rhs, res)
|
||||
return
|
||||
end
|
||||
|
||||
n0 = math.floor(n0/2);
|
||||
local n = n1+n2;
|
||||
|
||||
local a1 = number:new({});
|
||||
local tdata = a1.data; local sdata = lhs.data;
|
||||
for i = 1,n0 do tdata[i] = sdata[i] or 0 end; a1.size = n0;
|
||||
local a2 = number:new({}); tdata = a2.data;
|
||||
for i = n0+1,n1 do tdata[i-n0] = sdata[i] or 0 end; a2.size = n1-n0;
|
||||
|
||||
local b1 = number:new({});
|
||||
local tdata = b1.data; sdata = rhs.data;
|
||||
for i = 1,n0 do tdata[i] = sdata[i] or 0 end; b1.size = n0;
|
||||
local b2 = number:new({}); tdata = b2.data;
|
||||
for i = n0+1,n1 do tdata[i-n0] = sdata[i] or 0 end; b2.size = n1-n0;
|
||||
|
||||
--say("a1 " .. a1:tostring())
|
||||
--say("a2 " .. a2:tostring())
|
||||
|
||||
--say("b1 " .. b1:tostring())
|
||||
--say("b2 " .. b2:tostring())
|
||||
|
||||
|
||||
local A = number:new({}); number.karatsuba_multiply(a1,b1,A);
|
||||
local B = number:new({}); number.karatsuba_multiply(a2,b2,B);
|
||||
local C = number:new({}); number.karatsuba_multiply(a1+a2,b1+b2,C);
|
||||
--== C-A-B
|
||||
--TODO minus, reassemble together..
|
||||
|
||||
end
|
||||
|
||||
karatsuba_multiply_test = function()
|
||||
--local in2 = number:new({2,1,1})
|
||||
--local in1 = number:new({2,1,1})
|
||||
local res = number:new({});
|
||||
local in1 = number:new({3,1,4}); --413
|
||||
local in2 = number:new({7,2,5}); -- 527
|
||||
number.karatsuba_multiply(in1,in2,res)
|
||||
end
|
||||
|
||||
karatsuba_multiply_test()
|
||||
|
||||
|
||||
multiply_test = function()
|
||||
--local in2 = number:new({2,1,1})
|
||||
--local in1 = number:new({2,1,1})
|
||||
local res = number:new({});
|
||||
local in1 = number:new({4})
|
||||
number.multiply(in1,in1,res)
|
||||
say("mult check 1 " .. res:tostring())
|
||||
--say("mult check 2 " .. number.multiply(in1,in2):tostring())
|
||||
|
||||
end
|
||||
--multiply_test()
|
||||
|
||||
number.__mul = number.multiply;
|
||||
|
||||
number.power = function(n,power_) -- calculate high powers efficiently - number of steps is log_2(power)
|
||||
local power = power_;
|
||||
local input = number:new(n.data);
|
||||
local out = number:new({});
|
||||
|
||||
local count = 0;
|
||||
|
||||
local r; local powerplan = {}; -- 0: just square a->a^2, 1 = square and multiply a-> a*a^2
|
||||
while (power>0) do
|
||||
r=power%2; powerplan[#powerplan+1] = r; power = (power-r)/2
|
||||
end
|
||||
|
||||
for i = #powerplan-1,1,-1 do
|
||||
|
||||
number.multiply(input,input,out);
|
||||
|
||||
if powerplan[i] == 1 then
|
||||
input,out = out, input;
|
||||
number.multiply(input,n,out); count = count + 2
|
||||
else count = count + 1;
|
||||
end
|
||||
|
||||
input,out = out, input;
|
||||
end
|
||||
|
||||
return input
|
||||
end
|
||||
|
||||
split = function(s,k)
|
||||
local ret = "";
|
||||
local j=1,length; length = string.len(s)/k
|
||||
for i = 1, length do
|
||||
j = (i-1)*k+1;
|
||||
ret = ret .. string.sub(s,j,j+k-1) .. "\n"
|
||||
end
|
||||
--say("j " .. j)
|
||||
if j>1 then j = j+k end
|
||||
ret = ret .. string.sub(s,j)
|
||||
return ret
|
||||
end
|
||||
|
||||
self.spam(1)
|
||||
|
||||
-- little endian ! lower bits first ..
|
||||
|
||||
--n = number:new({7,1,0,2}); local power = 2017;
|
||||
--self.label(split(n:tostring().."^"..power .. " = " .. number.power(n,power):tostring(),100))
|
||||
--2017^2017 = 3906...
|
||||
end
|
|
@ -0,0 +1,13 @@
|
|||
-- title : Hello_World_1
|
||||
-- author :
|
||||
-- description : Say "Hello World", while spinning atop the spawner.
|
||||
-- source : https://wiki.minetest.net/Mods/basic_robot
|
||||
--
|
||||
|
||||
-- The robot will say "Hello World" again and again, while spinning atop the spawner.
|
||||
-- To stop it, rightclick the robot or spawner, and press the Stop-button.
|
||||
|
||||
say("Hello World")
|
||||
turn.left()
|
||||
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
-- title : Mining_103
|
||||
-- author : rnd/hajo
|
||||
-- description : A more advanced digger
|
||||
-- source : https://wiki.minetest.net/Mods/basic_robot
|
||||
--
|
||||
|
||||
-- rnd 2017-01-23 / hajo 2017-01-26
|
||||
if not commands then
|
||||
--turn.left() -- aim robot in a different direction
|
||||
--move.up()
|
||||
|
||||
script = "DuDdf" -- Variant 1 - uses only dig-forward
|
||||
-- script = "^Df" -- Variant 2: DigUp, DigForward, MoveForward
|
||||
|
||||
commands = {
|
||||
["f"] = function() move.forward() end,
|
||||
["b"] = function() move.backward() end,
|
||||
["l"] = function() move.left() end,
|
||||
["r"] = function() move.right() end,
|
||||
["u"] = function() move.up() end,
|
||||
["d"] = function() move.down() end,
|
||||
|
||||
["<"] = function() turn.left() end,
|
||||
[">"] = function() turn.right() end,
|
||||
|
||||
["D"] = function() dig.forward() end,
|
||||
["^"] = function() dig.up() end,
|
||||
}
|
||||
|
||||
i=1; n=string.len(script)
|
||||
end
|
||||
|
||||
c=string.sub(script,i,i); commands[c]();
|
||||
if i>=n then i=1 else i=i+1 end
|
|
@ -0,0 +1,20 @@
|
|||
-- title : Big_Display
|
||||
-- author :
|
||||
-- description : Show text from the book in slot#1 on a big screen.
|
||||
-- source : https://wiki.minetest.net/Mods/basic_robot
|
||||
--
|
||||
|
||||
|
||||
if not pn then pn="BigDisplay" -- show news on big display
|
||||
title,text = book.read(1);
|
||||
if not text then say("No text... \n Place a written book into the first slot of the library"); self.remove(); goto ex end
|
||||
|
||||
self.name("")
|
||||
self.label(title)
|
||||
self.display_text(text,20,3) --triple-size, 20 char per line
|
||||
|
||||
--turn.left() --turn into the direction of the player
|
||||
turn.right()
|
||||
end
|
||||
|
||||
::ex::
|
|
@ -0,0 +1,14 @@
|
|||
-- title : Find_you
|
||||
-- author :
|
||||
-- description : Find players in a 5 nodes radius.
|
||||
-- source : https://forum.minetest.net/viewtopic.php?f=3&t=18345
|
||||
--
|
||||
|
||||
|
||||
players = find_player(5);
|
||||
if players then
|
||||
msg = table.concat(players," and ")
|
||||
msg = "Around me i see " .. msg
|
||||
say(msg)
|
||||
self.remove()
|
||||
end
|
|
@ -0,0 +1,30 @@
|
|||
-- title : Ore-detector1
|
||||
-- author :
|
||||
-- description : Find ores in a 9 nodes radius.
|
||||
-- source : https://wiki.minetest.net/Mods/basic_robot
|
||||
--
|
||||
-- TIP : Run this detector on the robot, and use an 'empty' remote controller to move the bot around to find the good stuff.
|
||||
|
||||
|
||||
if (rg==nil) then s="Ore-detector v0.3"
|
||||
say(s)
|
||||
self.label(s)
|
||||
rg=9 i=1 next=0
|
||||
ore={'diamond', 'mese', 'gold', 'copper', 'iron', 'coal', '-'} --Table of valueable stuff
|
||||
end
|
||||
|
||||
if (ore[i]=="-") then next=1 i=0 end
|
||||
if next>0 then
|
||||
i=i+1 rg=9 next=0
|
||||
say("Seaching: "..ore[i])
|
||||
end
|
||||
if (rg>8) then rg=8 end
|
||||
|
||||
f = find_nodes("default:stone_with_"..ore[i], rg) -- returns true/false
|
||||
if f then
|
||||
say("Found "..ore[i].." within range "..rg)
|
||||
rg=rg-1
|
||||
else
|
||||
--say("no "..i.." @ rg"..rg)
|
||||
next=1
|
||||
end
|
|
@ -0,0 +1,48 @@
|
|||
-- title : Ore-detector2
|
||||
-- author :
|
||||
-- description : A faster, optimized version of the Ore-Detector1.
|
||||
-- source : https://wiki.minetest.net/Mods/basic_robot
|
||||
--
|
||||
-- TIP : Run this detector on the robot, and use an 'empty' remote controller to move the bot around to find the good stuff.
|
||||
-- It also searches for water and lava, to prevent surprises during mining.
|
||||
|
||||
if not s then s="Ore-detector2 v0.4f"
|
||||
say(s)
|
||||
self.label(s)
|
||||
rg=9 i=1 next=0 msg=""
|
||||
|
||||
--Table of stuff to search:
|
||||
ore={'@diamond', '@mese', '@gold',
|
||||
'@copper', '@iron', '@coal',
|
||||
--'!torch', '!torch_wall',
|
||||
'!lava_source', '!lava_flowing',
|
||||
'!water_source', '!water_flowing',
|
||||
'-'} --todo: moreores, mobs
|
||||
say(#ore) --
|
||||
end
|
||||
|
||||
if (ore[i]=="-") then next=1 i=0
|
||||
--say("done.") self.remove()
|
||||
end
|
||||
if next>0 then
|
||||
i=i+1 rg=9 next=0
|
||||
--msg="Seaching: "..ore[i] say(msg) msg=""
|
||||
end
|
||||
if (rg>8) then rg=8 end
|
||||
|
||||
s = ore[i]
|
||||
c1 = string.sub(s,1,1)
|
||||
s = string.sub(s,2)
|
||||
if c1=="@" then s="default:stone_with_"..s end
|
||||
if c1=="!" then s="default:" ..s end
|
||||
|
||||
f = find_nodes(s, rg) -- returns true/false
|
||||
if f then
|
||||
msg="Found "..s.." within range "..rg
|
||||
rg=rg-1
|
||||
else
|
||||
--say(i.." no "..s.." @ rg"..rg) --debug
|
||||
if #msg>0 then say("--> "..msg) end
|
||||
msg=""
|
||||
next=1
|
||||
end
|
|
@ -0,0 +1,39 @@
|
|||
-- title : Robot Penguin
|
||||
-- author : ixfud
|
||||
-- description : Robot that look like a penguin and interact with the player.
|
||||
-- source :
|
||||
--
|
||||
|
||||
-- Initialise
|
||||
if not i then
|
||||
i = 0
|
||||
move.left()
|
||||
-- Change le texte au dessus du Robot
|
||||
self.label("Robot Penguin")
|
||||
-- Definit le modele 3D
|
||||
self.set_properties({
|
||||
visual = "mesh",
|
||||
mesh = "mobs_penguin.b3d",
|
||||
visual_size = {x = 0.35, y = 0.35},
|
||||
collisionbox = {-0.2, -0.0, -0.2, 0.2, 0.5, 0.2},
|
||||
textures = {"mobs_penguin.png"}
|
||||
})
|
||||
elseif i < 6 then
|
||||
-- say(i.." - place")
|
||||
move.up()
|
||||
place.down("default:dirt")
|
||||
else
|
||||
under = read_node.down()
|
||||
if (( under ~= "basic_robot:spawner" )
|
||||
and (under ~= "default:obsidian_block")) then
|
||||
|
||||
dig.down()
|
||||
move.down()
|
||||
else
|
||||
i = 0
|
||||
turn.left()
|
||||
move.left()
|
||||
end
|
||||
end
|
||||
|
||||
i = i + 1
|
|
@ -0,0 +1,210 @@
|
|||
-- title : Cobble Stair
|
||||
-- author : ixfud
|
||||
-- description : Build cobble stairway from the bottom and dig if necessary. it needs cobble and tree to create energy, and optionnally torches.
|
||||
-- source :
|
||||
--
|
||||
|
||||
-- .
|
||||
-- TODO Change variable "tower_mode" to true to prevent digging while climbing
|
||||
-- TODO Change variable "spiral_mode" to false to clime a straigth line prevent digging while climbing
|
||||
|
||||
local c = "default:cobble"
|
||||
local t = "default:torch"
|
||||
local d = "default:dirt"
|
||||
local s = "stairs:stair_cobble"
|
||||
local p = self.pos()
|
||||
local r = 1
|
||||
|
||||
if not l then l = 1
|
||||
elseif l > 4 then
|
||||
-- return
|
||||
l = 1
|
||||
end
|
||||
|
||||
local e = machine.energy()
|
||||
say(e)
|
||||
|
||||
local cmb = {"default:tree", "default:pine_tree", "default:jungle_tree", "default:aspen_tree", "default:acacia_tree", "default:coal_lump"}
|
||||
if e < 1 then
|
||||
local src
|
||||
for _,s in ipairs(cmb) do
|
||||
if check_inventory.self(s) then src = s ; break ; end
|
||||
end
|
||||
if not src then
|
||||
say("I can't create energy ! Give me some wood or coal !")
|
||||
l = 404
|
||||
else
|
||||
say('Creating energy from '..src)
|
||||
machine.generate_power(src)
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
if check_inventory.self(c) == false then
|
||||
say("I need cobble !")
|
||||
return
|
||||
end
|
||||
if check_inventory.self(s) == false then
|
||||
say("Ho I have to craft some stairs ...")
|
||||
if craft(s) then say("... done")
|
||||
else
|
||||
l = 404
|
||||
say("Oups, I can't do that ! I may be short of cobble.")
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
if l == 1 then r = 1
|
||||
elseif l == 2 then r = 24
|
||||
elseif l == 3 then r = 3
|
||||
elseif l == 4 then r = 4
|
||||
end
|
||||
|
||||
if not a then a = "place_stair" end
|
||||
if not i then i = 1 end
|
||||
|
||||
if a == "place_stair" then
|
||||
if not hold then
|
||||
if i == 1 then say("Placing stairs ...")
|
||||
elseif i == 2 then move.left()
|
||||
elseif i == 3 then move.right() ; move.right()
|
||||
else
|
||||
move.left()
|
||||
a = "move_stair"
|
||||
i = 1
|
||||
return
|
||||
end
|
||||
else
|
||||
hold = nil
|
||||
end
|
||||
n = read_node.forward()
|
||||
if n ~= "air" then
|
||||
dig.forward()
|
||||
hold = true
|
||||
return
|
||||
else
|
||||
place.forward(s,r)
|
||||
i = i + 1
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
if a == "move_stair" then
|
||||
if i == 1 then dir = "up"
|
||||
elseif i == 2 then dir = "forward"
|
||||
end
|
||||
|
||||
n = read_node[dir]()
|
||||
if n ~= "air" then dig[dir]() ; return end
|
||||
move[dir]()
|
||||
|
||||
i = i + 1
|
||||
if i > 2 then
|
||||
a = "do_floor"
|
||||
i = 1
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
if a == "do_floor" then
|
||||
if not j then
|
||||
say("Let's make sure there is a floor")
|
||||
j = 1
|
||||
end
|
||||
|
||||
if j == 1 then dir = "forward_down"
|
||||
elseif j == 2 then
|
||||
n = read_node.forward()
|
||||
if n ~= "air" then dig.forward() ; return
|
||||
else move.forward(); dir="left_down"
|
||||
end
|
||||
elseif j == 3 then dir = "right_down"
|
||||
end
|
||||
|
||||
if i < 4 then
|
||||
n = read_node[dir]()
|
||||
-- say('I should place '.. c.. ' '..dir)
|
||||
if n == "air" then
|
||||
place[dir](c)
|
||||
-- say("Therefore I place "..c)
|
||||
end
|
||||
j = j + 1
|
||||
if j > 3 then
|
||||
j = 1
|
||||
i = i + 1
|
||||
end
|
||||
return
|
||||
elseif i > 6 then
|
||||
n = read_node.up()
|
||||
if n ~= "air" then dig.up()
|
||||
else
|
||||
move.up()
|
||||
a = "clear_floor"
|
||||
i = 1
|
||||
j = nil
|
||||
end
|
||||
return
|
||||
else
|
||||
move.backward()
|
||||
i = i + 1
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
if a == "clear_floor" then
|
||||
if not j then
|
||||
say("I will clear the floor now")
|
||||
j = 1
|
||||
end
|
||||
|
||||
where = {"down","up","left_up" , "right_up", "left","right", "left_down","right_down"}
|
||||
if j < ( #where + 1 ) then
|
||||
dir = where[j]
|
||||
while read_node[dir]() == "air" do
|
||||
j = j + 1
|
||||
if j > #where then
|
||||
j = ( #where + 1 )
|
||||
return
|
||||
else dir = where[j]
|
||||
end
|
||||
end
|
||||
dig[dir]()
|
||||
j = j + 1
|
||||
return
|
||||
else
|
||||
say('Slice '.. i .. '/4 ...done.')
|
||||
if i < 4 then
|
||||
n = read_node.forward()
|
||||
if n ~= "air" then dig.forward() ; return
|
||||
else
|
||||
move.forward()
|
||||
j = 1
|
||||
i = i + 1
|
||||
return
|
||||
end
|
||||
else
|
||||
if check_inventory.self(t) then place.right_down(t,1) end
|
||||
a = "turn_and_restart"
|
||||
i = 1
|
||||
j = nil
|
||||
say("Ok, let's get ready for the next step !")
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if a == "turn_and_restart" then
|
||||
if i == 1 then move.down()
|
||||
elseif i == 2 then move.backward()
|
||||
elseif i == 3 then move.left()
|
||||
else
|
||||
turn.left()
|
||||
l = l + 1
|
||||
i = nil
|
||||
a = nil
|
||||
return
|
||||
end
|
||||
i = i + 1
|
||||
return
|
||||
end
|
|
@ -0,0 +1,30 @@
|
|||
-- title : Hello_World_2
|
||||
-- author :
|
||||
-- description : Outputs "Hi !" (only once), then start counting and turning.
|
||||
-- source : https://wiki.minetest.net/Mods/basic_robot
|
||||
--
|
||||
|
||||
if (i == nil) then
|
||||
say("Hi !")
|
||||
self.label("Hugo")
|
||||
i = 0
|
||||
end
|
||||
|
||||
i = i+1
|
||||
turn.left()
|
||||
say(i)
|
||||
|
||||
|
||||
-- Outputs "Hi !" (only once), then start counting and turning.
|
||||
--
|
||||
-- == is the check to compare the values on both sides, i.e. "if i is-equal-to nil".
|
||||
-- = is an assignment, i.e. "i=0" means "put the value 0 into the variable i".
|
||||
|
||||
-- To calculate i+1, the variable i must have a value assigned first.
|
||||
-- And because all the code is executed again every second,
|
||||
-- this first assignment must be done only once.
|
||||
|
||||
-- (Otherwise, we couldn't count to 2)
|
||||
|
||||
-- At the start, no variables exist yet, so we can test for nil ("no-value").
|
||||
-- Note: nil is different from any string or number !
|
|
@ -0,0 +1,14 @@
|
|||
-- title : Hello_World_3
|
||||
-- author :
|
||||
-- description : Shorter version of Hello_World_2.
|
||||
-- source : https://wiki.minetest.net/Mods/basic_robot
|
||||
--
|
||||
|
||||
if i==nil then i="Hi !"; say(i); i=0 end
|
||||
i=i+1 say(i) turn.left()
|
||||
|
||||
-- Same as above, but with multiple statements in one line,
|
||||
-- and some variations (e.g. braces and ";" are optional).
|
||||
|
||||
-- In lua, the value of a variables can be of any type.
|
||||
-- Here, i first is assigned the string "Hi !", and later a number.
|
|
@ -0,0 +1,16 @@
|
|||
-- title : Catch_Me_1
|
||||
-- author :
|
||||
-- description : Some program that works perfectly well, but still shoots the player's foot :-)
|
||||
-- source : https://wiki.minetest.net/Mods/basic_robot
|
||||
--
|
||||
|
||||
dig.down()
|
||||
move.down()
|
||||
|
||||
-- The robot keeps digging down, and will continue unless it is stopped or it finds a cave, or an unloaded map-block.
|
||||
-- With the first dig, it will also destroy its spawner.
|
||||
-- So, to get at the stop-button, the user has to jump into the hole to get at the robot.
|
||||
-- (Quickly, otherwise the player also gets falling damage !)
|
||||
|
||||
-- Leaving the game, and entering again probably also stops the bot...
|
||||
-- (Maybe a bug: without the spawner, the inventory of the robot is not accessible)
|
|
@ -0,0 +1,22 @@
|
|||
-- title : Catch_Me_2
|
||||
-- author :
|
||||
-- description : Now we go up, instead of down. :-)
|
||||
-- source : https://wiki.minetest.net/Mods/basic_robot
|
||||
--
|
||||
|
||||
dig.up()
|
||||
move.up()
|
||||
local dirt_place = place.down("default:dirt")
|
||||
if not dirt_place then say("I need dirt in my inventory") end
|
||||
p = self.pos() -- returns a table, with elements x, y, and z
|
||||
say("Position: " .. p.x .. "," .. p.y .. "," .. p.z)
|
||||
|
||||
-- Now we go up, instead of down.
|
||||
-- After stopping the robot, and pressing start again,
|
||||
-- there will be a block of dirt above the bot from the first go.
|
||||
-- We need to dig away this block so that the bot can move up again.
|
||||
|
||||
-- The command "say()" outputs a single string, but several strings can be concatenated with '..'.
|
||||
-- Numbers are automatically converted to strings for output (e.g. the number in p.x).
|
||||
|
||||
-- Note: y is used as the height here in minetest.
|
|
@ -0,0 +1,19 @@
|
|||
-- title : Run_1
|
||||
-- author :
|
||||
-- description : The robot run and turn when hitting an obstacle
|
||||
-- source : https://wiki.minetest.net/Mods/basic_robot
|
||||
--
|
||||
|
||||
if (i==nil) then say("Demo1"); i=0 end
|
||||
|
||||
if read_node.forward()=="air" then
|
||||
move.forward()
|
||||
else
|
||||
turn.right()
|
||||
end
|
||||
|
||||
-- If nothing ("air" mean nothing solid) is in front of it, the robot moves forward, otherwise it turns.
|
||||
-- The robot can "fly" one block above the ground, like the player can reach by jumping up. The bot just doesn't need to bounce up & down.
|
||||
|
||||
-- That means, if the robot reaches a place where the ground one step ahead is more then one block lower, he cannot move forward.
|
||||
-- With the above program, he just stops moving, because there are no instructions in the code to handle that case.
|
|
@ -0,0 +1,49 @@
|
|||
-- title : Run_2
|
||||
-- author :
|
||||
-- description : Essentially the same program as 'Run_1', but with more checking and reporting
|
||||
-- source : https://wiki.minetest.net/Mods/basic_robot
|
||||
--
|
||||
|
||||
local function report_pos(msg,complement)
|
||||
p = self.pos()
|
||||
say(msg .. " position x=" .. p.x .. " y=" .. p.y .. " z=" .. p.z .. "/n" .. complement)
|
||||
end
|
||||
--
|
||||
if (i==nil) then say("\nDemo2"); i=0 end
|
||||
--
|
||||
n = read_node.forward()
|
||||
if n~="air" then
|
||||
report_pos("Now at")
|
||||
say("found "..n..", turning")
|
||||
turn.right()
|
||||
else
|
||||
if move.forward() then
|
||||
i=i+1 -- move was ok
|
||||
else
|
||||
report_pos("Cannot move at","(probably because I would need to fly to go there)")
|
||||
--self.remove() -- stop program, and remove robot
|
||||
end
|
||||
end
|
||||
|
||||
if i > 20 then
|
||||
say("After 20 steps, I turn without any reason, this way I avoid getting stuck")
|
||||
if math.random(1,2) == 1 then turn.right() else turn.left() end
|
||||
i=1
|
||||
end
|
||||
|
||||
-- This is essentially the same program as 'Run1' above, but with more checking and reporting:
|
||||
-- now there is a check if the move was successful, and output to report the position.
|
||||
--
|
||||
-- Because we want to report the current position of the bot at more then one place in the code,
|
||||
-- the common instructions for that have been moved into a function.
|
||||
-- When called, the function gets a string as a parameter, that is used in the message that is generated.
|
||||
-- There is no return-statement at the end of the function, because we don't need a result.
|
||||
|
||||
-- ".." is the lua-command for concatenating strings.
|
||||
-- "\n" is newline.
|
||||
-- "~=" is the check for not-equal.
|
||||
-- "--" starts a comment. Text after this until the end of the line is ignored.
|
||||
|
||||
-- Here, we have also commented-out the statement "self.remove()",
|
||||
-- so that it doesn't get executed.
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
-- title : Mining_101
|
||||
-- author :
|
||||
-- description : Simple digger 1x1
|
||||
-- source : https://wiki.minetest.net/Mods/basic_robot
|
||||
--
|
||||
|
||||
-- simple digger1x1
|
||||
if not i then -- Init:
|
||||
i=0
|
||||
turn.left() -- initital positioning: aim robot in a different direction
|
||||
--move.up()
|
||||
end
|
||||
|
||||
-- Work:
|
||||
i=i+1 say(i)
|
||||
if i<=20 then
|
||||
dig.forward()
|
||||
move.forward()
|
||||
end
|
|
@ -0,0 +1,23 @@
|
|||
-- title : Mining_102
|
||||
-- author :
|
||||
-- description : simple digger 2x1
|
||||
-- source : https://wiki.minetest.net/Mods/basic_robot
|
||||
--
|
||||
|
||||
-- simple digger 2x1
|
||||
if not i then -- Init:
|
||||
i=0
|
||||
turn.left() -- initital positioning: aim robot in a different direction
|
||||
--move.up()
|
||||
end
|
||||
|
||||
-- Work:
|
||||
i=i+1 say(i)
|
||||
if i<=20 then
|
||||
dig.forward()
|
||||
-- ??
|
||||
dig.up() -- !! doesn't work with setting 'maxdig=1'
|
||||
move.forward()
|
||||
else
|
||||
say("stop") self.remove()
|
||||
end
|
|
@ -0,0 +1,11 @@
|
|||
bugs :
|
||||
Return compilation errors for commented words :
|
||||
repeat not allowed! because some comment contains the word 'repeatedly' (exemple1)
|
||||
until not allowed! because some comment contains the word 'until' (exemple4)
|
||||
|
||||
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
|
||||
=²exemple 5 - catch_me_2 Ajouter retour "I need dirt !"
|
||||
exemple 7 - run_2 Remplacer "cannot move" with explaination, bug until
|
||||
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
|
||||
exempe 9 - 11 - mining request energy
|
||||
exempe 10 - mining102 out of available operation
|
|
@ -0,0 +1,64 @@
|
|||
#!/bin/sh
|
||||
# Generate a html page from all the markdown documents present in rep 'docs'
|
||||
# Require 'markdown' command
|
||||
#
|
||||
|
||||
#-- Markdown docs path
|
||||
docpath="../docs"
|
||||
if [ ! -d "$docpath" ];then exit ;fi
|
||||
|
||||
#-- Temporary files
|
||||
html_menu_tmp="~html_doc_menu.tmp"
|
||||
html_content_tmp="~html_doc_content.tmp"
|
||||
if [ -f "$html_menu_tmp" ];then
|
||||
echo "The temporary destination file '$html_menu_tmp' already exist. Aborting"
|
||||
exit
|
||||
fi
|
||||
if [ -f "$html_content_tmp" ];then
|
||||
echo "The temporary destination file '$html_menu_tmp' already exist. Aborting"
|
||||
exit
|
||||
fi
|
||||
#-- output file
|
||||
output="../robot_doc.html"
|
||||
# if [ -f "$output" ];then echo "The output file '${output}' already exist"; exit; fi
|
||||
|
||||
#-- Initialize files
|
||||
echo "<h1>Robot Documentation</h1>" > "$html_menu_tmp"
|
||||
echo "" > "$html_content_tmp"
|
||||
echo "<a name='top'></a><ul>" >> "$html_menu_tmp"
|
||||
|
||||
|
||||
#-- Loop through file
|
||||
files=$(ls $docpath |grep -e "^robot_doc_.*\.md$")
|
||||
for mddoc in $files ; do
|
||||
#-- Full path
|
||||
doc="${docpath}/${mddoc}"
|
||||
|
||||
#-- Get infos
|
||||
pat_title="\-\- title :"
|
||||
title=$(grep "$pat_title" $doc |sed "s#$pat_title##")
|
||||
pat_description="\-\- description :"
|
||||
description=$(grep "$pat_description" $doc |sed "s#$pat_description##")
|
||||
|
||||
#-- Menu entry
|
||||
echo " <li><a href=\"#${mddoc}\">${title}</a>" >> "$html_menu_tmp"
|
||||
echo " <br> - ${description}</li>" >> "$html_menu_tmp"
|
||||
|
||||
#-- Anchor
|
||||
echo "<hr>" >> "$html_content_tmp"
|
||||
echo "<a name=\"${mddoc}\"></a><a href=\"#top\">-- Return to Index --</a><br>" >> "$html_content_tmp"
|
||||
|
||||
#-- Content
|
||||
markdown "$doc" >> "$html_content_tmp"
|
||||
echo "" >> "$html_content_tmp"
|
||||
done
|
||||
echo "</ul>" >> "$html_menu_tmp"
|
||||
|
||||
#-- Join files
|
||||
echo "" > "$output"
|
||||
cat "$html_menu_tmp" >> "$output"
|
||||
cat "$html_content_tmp" >> "$output"
|
||||
|
||||
#-- Delete temporary files
|
||||
rm -f "$html_menu_tmp"
|
||||
rm -f "$html_content_tmp"
|
|
@ -0,0 +1,54 @@
|
|||
#!/bin/sh
|
||||
# Rename scripts to be readable by lua as <prefix>_<number>.lua
|
||||
# And add title and author as comment at the beginning of the file
|
||||
#
|
||||
# Arguments :
|
||||
### 1 : path of directory containing scripts
|
||||
### 2 : prefix
|
||||
### 3 : number to start numerotation
|
||||
### 4 : author of the scripts
|
||||
### 5 : source of the scripts
|
||||
### 6 : description of the scripts
|
||||
|
||||
|
||||
|
||||
dirpath="$1"
|
||||
prefix="$2"
|
||||
n="$3"
|
||||
author="$4"
|
||||
source="$5"
|
||||
description="$6"
|
||||
help="Usage :
|
||||
rename_scripts <folder_containing_scripts> <prefix> <number_to_start_numerotation> <author_of_scripts> <source_of_scripts> <description_of_scripts>"
|
||||
|
||||
#-- Return if not a valid directory
|
||||
if [ ! -d "$dirpath" ]; then
|
||||
echo "Wrong argument 1 (directory path) :'$dirpath' "
|
||||
echo "$help"
|
||||
exit
|
||||
fi
|
||||
#-- Return if prefix is empty
|
||||
if [ -z "$prefix" ]; then
|
||||
echo "Argument 2 (prefix) is empty and must be definined"
|
||||
echo "$help"
|
||||
exit
|
||||
fi
|
||||
#-- Set n to 1 if empty
|
||||
if [ -z "$n" ]; then n=1 ;fi
|
||||
|
||||
for scp in $(ls $dirpath |grep -e "\.lua$"); do
|
||||
title=$(basename --suffix=.lua $scp)
|
||||
in="${dirpath}/${scp}"
|
||||
out="${dirpath}/${prefix}_${n}.lua"
|
||||
|
||||
echo "-- title : $title" > "$out"
|
||||
echo "-- author : $author" >> "$out"
|
||||
echo "-- description : $description" >> "$out"
|
||||
echo "-- source : $source" >> "$out"
|
||||
echo "--" >> "$out"
|
||||
echo "" >> "$out"
|
||||
cat "$in" >> "$out"
|
||||
|
||||
rm "$in"
|
||||
n=$((n+1))
|
||||
done
|
After Width: | Height: | Size: 178 B |
After Width: | Height: | Size: 860 B |
After Width: | Height: | Size: 1.7 KiB |
After Width: | Height: | Size: 771 B |
After Width: | Height: | Size: 2.9 KiB |
After Width: | Height: | Size: 416 B |
After Width: | Height: | Size: 892 B |
After Width: | Height: | Size: 329 B |
After Width: | Height: | Size: 395 B |
After Width: | Height: | Size: 597 B |
After Width: | Height: | Size: 744 B |
After Width: | Height: | Size: 561 B |
After Width: | Height: | Size: 288 B |
After Width: | Height: | Size: 187 B |
After Width: | Height: | Size: 186 B |
After Width: | Height: | Size: 174 B |
After Width: | Height: | Size: 175 B |
After Width: | Height: | Size: 224 B |