- Added per-command sandbox networking with run_shell(network=true).
- Added optional per-command shell timeouts, configurable from 1 second up to 1 hour.
- Added /shell [network] for opening an interactive shell inside Talos’s sandbox.
- Added separate approval and pre-approval handling for networked sandbox commands.
- Improved shell safety documentation, including sandbox-home isolation and approval requirements.
- Removed redundant status messages for pre-approved shell commands.
- Fixed large shell outputs potentially blocking commands by continuing to drain discarded output after reaching the display limit.
- Made workspace snapshots respect ignored files and ignored directory trees.
- Added default workspace exclusions for common Python, Java, Gradle, Maven, and cache directories.
- Updated ignored-path documentation to clarify that ignores affect both file tools and workspace-change notices.
- Disabled Lua CJSON’s retained encoding buffer to reduce persistent memory usage after encoding large state.
- Added full Lua garbage collection before displaying each main user prompt.
- Improved numeric tool-call displays so integral values no longer appear with a .0 suffix.
- Improved shell help text so network-approval instructions appear only when network confirmation is enabled.
- Clarified the source header’s instructions for finding configuration and behavior sections.
Sunday, September 20, 2026
Talos 1.1.0
Tuesday, July 28, 2026
Talos v1.0.0
After using Gemini-cli at work, I thought I'd try some coding agent also for my personal projects. I don't trust running them locally on my computer for privacy reasons, so instead I thought I'd run the agent on a small VPS I rent, 1GB of RAM, 1 CPU core.
I quickly discovered that just installing OpenCode pretty much killed that VPS. Those Gemini-cli based agents full node.js applications, with a myriad different dependencies. I started looking for some lightweight agent but even the most lightweight I could find (pi.dev) still seemed too much for me.
So I made my own. I used a handwritten agent I was using locally in the past for some experimentation and changed it a bit to bootstrap rudimentary coding agent out of it. Then I used itself to continue developing it.
I named it after Talos, it's written in Lua as a single hackable .lua file and it targets Linux. I suggest you add your API key to it by editing the script (it's hardcoded in it), drop the script in your ~/bin directory (or anywhere in your path), copy it also in an empty directory, and then start Talos inside the empty directory and ask it to modify itself. Then use your favorite tool to review the changes it makes, for example I like using Meld to view the differences between the running version and the modified version and to accept them into the running version before restarting it. If you need help, drop a comment here.
To run it you need to install Lua and the following Lua modules which you should be able to find in your repositories: socket ssl cjson ltn12 lfs posix readline
Lfs may be called luafilesystem or something like that in your repos. Posix and readline are optional but strongly recommended. Ltn12 and ssl may be part of luasec. Socket is the usual luasocket.
You will also need to make sure you have the following commands available: sh, timeout, tar, zstd, diff, bwrap, stty
Bwrap is bubblewrap, which is a chunky one and the reason why Talos doesn't work on anything but Linux. It is used for sandboxing so it's likely the most important dependency.
Here is the help page of Talos which should explain also most of its features:
Talos - interactive LLM agent
=============================
Talos is a command-line AI agent. Type a message at the prompt and press Enter to talk to it. Talos can reason, hold a conversation, and act on your working directory by calling tools (reading/writing files, running shell commands, searching, and more). It remembers context across sessions.
GETTING STARTED
- Just type and press Enter to send a message.
- Press Enter on an empty line to quit.
- For multiline input, start with Lua long-bracket syntax like [[, [=[, or [===[. Any number of equal signs is acceptable, as long as the closing marker matches: ]], ]=], or ]===]. The markers are stripped, and everything between them is sent as one message.
- Start with an optional session name: talos mysession
Each named session keeps separate history and memory under .talos/session-<session_name>/. With no name, .talos/ is used.
WHAT TALOS CAN DO
- Hold a conversation and answer questions.
- Read, create, delete, search, and manipulate files in the working dir.
- Run sandboxed shell commands, and unsandboxed shell commands with your approval.
- Automatically resummarize the conversation if it grows too long, compressing messages into it's persistent memory.
SHELL COMMAND SAFETY
Normal run_shell commands are sandboxed inside the working directory and have no network. Unsandboxed run_shell commands can access host paths/network and require per-command approval or exact pre-approval:
y / yes - run it once and show output to Talos
n / no - decline
a / always - run and pre-approve this exact command
p / preview - run it, show you the output, then ask whether Talos may see the output
h / help - explain the options
:reason - decline, and send reason to Talos
Commands are killed if they run longer than 60 seconds.
PATH SAFETY
All file tools are confined to the working directory. Absolute paths and paths that escape the directory (via ..) are rejected. Only unsandboxed shell commands should be able to escape the working directory, and those need your approval to run.
SLASH COMMANDS
General
/help Show this guide.
/test Run live API self-tests.
/leaks Print a sandbox leak report locally; the report is not sent to the model.
Backups
/backup Write a compressed tar archive to .talos/backups/. Backups include everything in your working directory, including Talos's state.
Talos also creates an automatic backup before conversation turns if the newest backup is over 30 minutes old.
/backup list List backups from oldest to newest.
/backup prune Delete half the backups while keeping the latest backup, then show total backup size.
/backup restore N Restore backup number N, after making a safety backup, then quit.
/diff [N] Show a unified diff between the current workspace and backup N, or the latest backup.
Sandbox mounts
/mount list List persistent sandbox mounts.
/mount ro SRC DST Mount host SRC read-only at sandbox DST.
/mount rw SRC DST Mount host SRC read-write at sandbox DST.
/mount tmpfs DST Mount temporary writable memory at sandbox DST.
/mount remove N Remove sandbox mount number N.
Skills
/skill list List configured skill search paths and discovered skills.
/skill add PATH Add a skill search path with symlinks blocked. Defaults are .agents/skills/ and ~/.agents/skills/.
/skill addsl PATH Add a trusted skill search path with symlinks allowed.
/skill remove N Remove configured skill search path number N.
Memory and history
/summarize Fold older messages into long-term memory now.
/summarize all Fold all messages into long-term memory now.
/memory Print the current long-term memory.
/clear messages Erase the conversation history.
/clear memory Erase the long-term memory.
/clear cost Reset the session's cost.
/clear pre-approved Erase pre-approved shell commands.
/clear home Erase the persistent sandbox home directory.
/clear all Erase messages, memory, cost, pre-approved commands, and sandbox home.
Transfer
/upload PORT DIR Open a temporary browser upload page on PORT that saves files under DIR; use the page's stop button to end the command.
/download PORT FILE Open a temporary browser download page on PORT that serves FILE; use the page's stop button to end the command.
COST
Each session tracks its API cost, shown after responses and on startup.
You can download Talos from here.
Sunday, April 13, 2025
Slow Query Monitoring in MySQL and MariaDB
To enable slow query monitoring in MySQL or MariaDB, run these commands in your client:
SET GLOBAL slow_query_log = 1;
SET GLOBAL long_query_time = 0.5;
This activates the slow query log and sets it to capture any queries taking longer than 0.5 seconds. You can adjust this threshold based on your performance requirements.
Slow queries will be logged to some file like:
/var/lib/mysql/*-slow.log
You can check or change that with:
SHOW VARIABLES LIKE 'slow_query_log_file';
SET GLOBAL slow_query_log_file = '/custom/path/slow-queries.log';
To make these changes permanent, add these lines to your my.cnf/my.ini file:
[mysqld]
slow_query_log = 1
long_query_time = 0.5
slow_query_log_file = /custom/path/slow-queries.log
To analyze slow queries, use:
mysqldumpslow /custom/path/slow-queries.log
Wednesday, April 24, 2024
Voyager Manager v0.1
I've written around seven programs for my HP 12c. I've manually combined them into a big "multiprogram" which starts with a jump table: a bunch of GTO commands, each one jumping to the entry point of one of the actual programs. The reason for that is that the HP 12c has only one contiguous program area. Fortunately, the calculator allows you to manually execute a GTO command before pressing R/S to start the execution. But instead of having to remember the starting address of each program, I just put them on the GTO instructions on the jump table so I just need to manually do GTO 003 and press R/S to start the third program, because address 003 has the GTO to the starting point of the third program.
But managing this multiprogram is very tedious. When a program is placed in a different starting address in memory, all the GTO targets of that program need to be updated and offset correctly to continue working. So I made a program called Voyager Manager that lets me compile any number of programs into one such multiprogram, taking care of the GTOs and the jump table automatically. It works for my programs but it's still a very early version. It comes with GNU/GPLv3 source code, Lua binaries for Windows included, and a library that includes my programs for the HP 12c to get you started.
You can download Voyager Manager from here.
Wednesday, April 13, 2011
Quick note: Why I use Firefox
- Loading custom spell-checking dictionaries to Firefox allows me to spell-check both Greek and English at the same time, without even switching dictionaries (someone has already made a combined dictionary for that).
- There is a master password to protect the passwords in the password manager of Firefox. The Chrome guys for some reason refuse to implement that giving some ridiculous explanation on why they don't want to do it. (I can explain why it's ridiculous if someone disagrees)
BTW that makes Firefox Portable a great choice in portable browsers. - Firefox can change proxies easily while Chrome depends mostly on the system wide proxy settings (like IE) or some, not so practical, tricks.
- TorButton is only for Firefox.
- When I am making a webpage, the first browser on which it works as expected is Firefox. I've had some minor problems with Chrome when, for example, I was trying to implement a CSS sticky footer. Chrome would make the page a little bigger than the window thus showing a vertical scrollbar although there was no reason. Maybe it was some mistake I did but I had no problems in Firefox.
For some reason Firefox, usually, is the one that shows things as I expected them to be rendered, despite Chrome being more standards compliant. - Nothing compares to FireBug when you develop stuff... NOTHING.
- Although I still haven't done some serious tests, by my personal experience I find Firefox more responsive, compared to Chrome, when many tabs are open. But, usually, people don't work with 25+ tabs open at the same time like I do, so Chrome will be more responsive for normal usage.
Hey this note wasn't that quick after all...
Friday, January 16, 2009
Email sanitizer-extractor in Lua
To make it more interesting I managed to make it a single call program: everything is defined inside the arguments of a single call. In fact there are two calls: the first returns an object, a function of which is immediately called. Anyway here is the code:
io.open("output.txt","w"):write((string.gsub(" "..io.open(((arg or {})[1] or "input.txt")):read("*a").." ",".-([%w%.%-_]+@[%w%.%-_]+).-",function (email) email=string.lower(email) print("EMAIL: '"..email.."'") emails=emails or {} for index,emailseen in ipairs(emails) do if emailseen==email then return "" end end table.insert(emails,email) return email.."\n" end)))
Now let's break that up and put some comments, shall we?
io.open("output.txt","w"):write( --io.open opens a file in write mode and returns the file handle which instead of being stored in a variable is immediately used by calling it's filehandle:write function.
(string.gsub( --string.gsub will finally return two arguments: the email list, one per line, lowercase, and without duplicates and the number of replacements it did. The first argument is our output. I can discard the second by putting the function call, therefore the returned argument list, into parentheses. In Lua print((5,"kostas","klapatsimpala")) will just print 5.
" "..io.open(((arg or {})[1] or "input.txt")):read("*a").." " --This is the first argument to string.gsub. like we did before, we open a file in read mode and immediately use the returned handle to do a full read of the file. A tricky part is the "(arg or {})[1] or "input.txt"" part. If you call a lua script with extra arguments then the arg table will be created by Lua. If it exists then the "arg or {}" part will evaluate in "arg" (if on the left side of an "or" is a true value then "or" simply results in that) and then "(arg)[1]" will return the first variable which is a custom input filename. That filename "ORed" with "input.txt" will simply return that filename (since any strings are true values for Lua, so OR will evaluate in the left argument). If you didn't call the script with any arguments then the arg table will not exist, thus the "(arg or {})" part will result in a newly created empty table. Of course if you index it's first cell you'll find nothing, so the "({})[1] or "input.txt"" will result in "input.txt" (if "or" finds a false or nil value on it's left it will simply return the value on it's right). Finally I add two space characters: one to the start of the read data and one to the end. These are added so that the pattern matching I use will apply to any emails exactly at the beginning or exactly at the end of the read data.
,".-([%w%.%-_]+@[%w%.%-_]+).-" --The second argument is the pattern. I am breaking up the whole text in the following way: any number of any characters (as less as possible) followed by any number of email allowed characters (as much as possible), followed by @, followed by any number of email allowed characters (as much as possible), followed by any number of any characters (as less as possible). The "email allowed characters" are: alphanumerics, dot, dash, underscore). From this pattern I want to capture just the email part.
,function (email) --Now this is the best part. An anonymous function. It is created without being stored in a variable (which would give it a name) and immediately used as an argument to string.gsub. This function accepts a single argument: email. string.gsub will call it for every match with the capture (the email) as an argument.
email=string.lower(email) --First of all we turn the email to lowercase
print("EMAIL: '"..email.."'") --Debugging message...
emails=emails or {} --Remember what we said. If the left argument is true (not false and not nil) then it is returned, so if the emails variable has already been defined nothing will happen because emails=emails will be executed. If the emails variable is not defined (is nil) though, then "or" will return it's right argument therefore emails={} will be executed and emails will be initialized as an empty table.
for index,emailseen in ipairs(emails) do --For every already seen email do:
if emailseen==email then return "" end --If this already seen email is the same with the new capture then just return "" so that the whole match will be replaced by nothing. Remember that although the capture is just the email, the match includes the email as well as the preceding and the following characters.
end --end for.
table.insert(emails,email) --If we managed to get here then this email capture is seen for the first time. We insert it in the emails table.
return email.."\n" --and finally we return the email capture (lowercase) followed by a newline. This will replace the whole match.
end --end of the anonymous function
) --closing of string.gsub
) --that's the second parentheses for string.gsub (to discard the second returned argument)
) --closing of write.
That's all. I seem like it is working but I haven't done any extensive debugging.
Thursday, July 31, 2008
Big table implementation in Brainfuck
Also, all known implementations had a limit of 256 cells (Or equal to the size of a cell on the Brainfuck array. For the rest of the post I will assume that the cell size is one byte).
During my road-trip to Spain, based on my old implementation, I found a new one that can use multiple indexes to implement tables of ANY size on the Brainfuck array. Additionally the table cell size can be any multiple of the Brainfuck array's cell size.
It uses a format similar to my old implementation: a head moving through the table while decreasing the index it carries, passing table data over it, until it finds the correct position,the it does it's job (read/write) and then it returns in the same way but using a copy of the index.
For a table with n cells of size k each the form of the header is:
- k empty cells: When the head need's to move to the right, it will have to move the first k cells on it's right in these empty cells before moving itself k positions to the right (thus moving itself to the next indexed position)
- n index cells: Before starting a read or write operation you will have to store the index here. The leftmost index is the most important. For example if the maximum possible value for an index is 9 (instead of 255) and you use two indexes, to access the 15th cell you would have to use 1 for the left index and 4 for the right index (not 5 because the numbering starts from 0).
- k data cells: To write to the table you will have to put the data in these cells. If you read from the table, after the end of the read operation the read data will be here. These cells MUST be cleared before starting a read operation.
- n empty (index) cells: While the index cells are reduced, these cells are increased so that, when the seek operation finishes, they will hold a copy of the original indexes which will be used for returning the head to the beginning of the table.
After the header the table data is assumed to be laying. n*k cells in total. For example for a size 400 table with 2 bytes per cell they would have this form:
Data 1 of index 0,0
Data 2 of index 0,0
...
Data 1 of index 0,255
Data 2 of index 0,255
Data 1 of index 1,0
Data 2 of index 2,0
Data 1 of index 1,1
Data 2 of index 2,1
...
...
Data 1 of index 1,143
Data 2 of index 1,143
So you will need 2*k+2*ceil(log256(n))+n*k cells on the Brainfuck array to store a table with n, k-sized cells. (ceil(log256(n)) is the number of the indexes)
Therefore the code needed to write or read from the table depends on n and k. I actually wrote some metacode that provided with n and k produces the corresponding code. I then turned the metacode into a Lua script. It's not well written but I believe that it works. I tried the following cases:
- Writing two values to a table with n<256 and k=1 and then reading them back.
- Writing two values to a table with 256<n<256^2 and k=2 and then reading them back.
If you want to try the script yourself you download it from here.
Here is some example code:
n<256 k=1
Write:
>[->>+>[-<<<<+>>>>]<[->+<]<[->+<]
<[->+<]>]>>>[-]<<[->>+<<]>[-[-<+>
]<<<<[->>>>+<<<<]>>>]<<<
Read:
>[->>+>[-<<<<+>>>>]<[->+<]<<[->+<
]>]>>>[-<<+<<+>>>>]<<<<[->>>>+<<<
<]>>>[-<[-<+>]>[-<+>]<<<<[->>>>+<
<<<]>>>]<<<
256<n<256^2 k=1
Write:
>>[->>>+>[-<<<<<<+>>>>>>]<[->+<]<
[->+<]<[->+<]<[->+<]<[->+<]>>]<[-
>>>+>>[-<<<<<<+>>>>>>]<[->+<]<[->
+<]<[->+<]<[->+<]<[->+<]>>-[->>>>
[-<<<<<<+>>>>>>]<[->+<]<[->+<]<[-
>+<]<[->+<]<[->+<]>>]<]>>>>>[-]<<
<[->>>+<<<]>>[-<[-<+>]>[-<+>]<<<<
<<[->>>>>>+<<<<<<]>>>>>]<[-[-<+>]
>[-<+>]<<<<<<[->>>>>>+<<<<<<]>>>>
>-[-<[-<+>]>[-<+>]<<<<<<[->>>>>>+
<<<<<<]>>>>>]<]<<<<
Read:
>>[->>>+>[-<<<<<<+>>>>>>]<[->+<]<
[->+<]<<[->+<]<[->+<]>>]<[->>>+>>
[-<<<<<<+>>>>>>]<[->+<]<[->+<]<<[
->+<]<[->+<]>>-[->>>>[-<<<<<<+>>>
>>>]<[->+<]<[->+<]<<[->+<]<[->+<]
>>]<]>>>>>[-<<<+<<<+>>>>>>]<<<<<<
[->>>>>>+<<<<<<]>>>>>[-<<[-<+>]>[
-<+>]>[-<+>]<<<<<<[->>>>>>+<<<<<<
]>>>>>]<[-<[-<+>]>[-<+>]>[-<+>]<<
<<<<[->>>>>>+<<<<<<]>>>>>-[-<<[-<
+>]>[-<+>]>[-<+>]<<<<<<[->>>>>>+<
<<<<<]>>>>>]<]<<<<
Monday, February 18, 2008
Memory efficient Brainfuck tables
The table has the following structure:
0 I I D (0) (1) (2) (3) ...
^
For a table with n cells you will need n+4 cells on the Brainfuck array.
( n <= cell size) We start from where the ^ points. The first cell should be empty (zero). The second and third cells contain the index that we want to write to or read from. The fourth cell contains the data that we want to store in the table. If we want to read from the table then we should leave this cell empty and it will later carry the read data. The next cells (0) (1) ... (n-1) are the actual data stored in the table. We'll call the I I D part the "head". It works like this: Move the head to the right place: (the head moves to the right by moving the data of the next cell to the head's preceding empty cell, then moving each of the I I D cells one place to the right creating a new empty preceding cell.)
We will move the head to the right to the correct index:
>[
>>>[-<<<<+>>>>]
<
If we want to write something then we should carry the D cell too: [->+<]
<[->+<]
<[->+<]
>-]
Now we may read:
>>>[-<+<<+>>>]<<<[->>>+<<<]>
Or we may write:
>>>[-]<[->+<]<
Now we will return the head to it's initial position:
[
[-<+>]
If we have read something then we need to carry the D cell too: >[-<+>]<
<<<[->>>>+<<<<]
>>-]
<<
After a successful read the I I cells should be empty while the D cell will hold the read data.
After a successful write the I I D cells should be empty.
The pointer ends at the some place where it was before we used the algorithm, that is to the left of the first I cell.
Here is the code uncommented:
Write:
>[>>>[-<<<<+>>>>]<[->+<]<[->+<]<[->+<]>-]
>>>[-]<[->+<]<
[[-<+>]<<<[->>>>+<<<<]>>-]<<
Read:
>[>>>[-<<<<+>>>>]<<[->+<]<[->+<]>-]
>>>[-<+<<+>>>]<<<[->>>+<<<]>
[[-<+>]>[-<+>]<<<<[->>>>+<<<<]>>-]<<
Saturday, February 16, 2008
Tiny Lua Distribution
Sunday, January 20, 2008
SEcure PUblic Key TRAnsfer
- Each of the two parts generate a pair of public - private keys.
- The two parts exchange their public keys over a possibly insecure communication channel.
- Now if one part wants to send a message to the other it simply encrypts it with the public key of the other part and then sends it. An eavesdropper cannot decrypt this message because he has not got the private key needed.
- Bob generates a keypair. (a public and a private key) and sends his public key to Alice.
- Mario, who is an employer in Bob's ISP, has already generated a keypair and while the packet with Bob's key is transient he replaces Bob's key with his own (he also notes down Bob's public key).
- Alice receives Mario's public key but she believes it's Bob's.
- Alice generates a keypair and sends her public key to Bob but Mario replaces it with his own, just like he did with Bob's.
- Now Bob wants to send some data to Alice. He encrypts it with Mario's key believing that it's Alice's key and sends the encrypted data.
- The encrypted data is peaked by Mario who is able to decrypt them with his own private key. He then re-encodes them with Alice's correct public key and passes them on Alice.
- The same thing happens when Alice replies to Bob. Mario is able to eavesdrop on the connection even though both part's think that is secure.
I tried to understand the algorithm but I couldn't! And I think that it's too complex for no reason... They could use this protocol to achieve the same thing:While data messages are being exchanged, either Alice or Bob may run SMP to detect impersonation or man-in-the-middle attacks. As above, all exponentiations are done modulo a particular 1536-bit prime, and g1 is a generator of that group. All sent values include zero-knowledge proofs that they were generated according to this protocol, as indicated in the detailed description below.
Suppose Alice and Bob have secret information x and y respectively, and they wish to know whether x = y. The Socialist Millionaires' Protocol allows them to compare x and y without revealing any other information than the value of (x == y). For OTR, the secrets contain information about both parties' long-term authentication public keys, as well as information entered by the users themselves. If x = y, this means that Alice and Bob entered the same secret information, and so must be the same entities who established that secret to begin with.
- Bob knows X and Alice knows Y and they want to compare them.
- Bob sends some random data to Alice. Alice does the same with some other random data.
- Bob appends X to the end of the data he received and then computes the hash of the new data. Alice appends Y to the end of the data she sent to Bob and computes the hash of the new data. Bob sends the hash to Alice. Alice checks whether the two hashes are the same.
- Alice appends Y to the end of the data she received and then computes the hash of the new data. Bob appends X to the end of the data he sent to Alice and computes the hash of the new data. Alice sends the hash to Bob. Bob checks whether the hashes are the same.
- H(x) is the hash of x. Any hash function can be used. Take a look at MD5.
- E(x,k) is the symmetrically encrypted data of x with k used as the encryption key.
- DE(x,k) is the symmetrically decrypted data of x with k used as the decryption key.
- PK is the public key of the sender.
- RDP means Random Data Padding.
- & is the concatenation symbol (VB-style :-P).
- len(x) is the length in bytes of x.
- D: The Data packet. It is composed of 4 parts: len(RDP) & RDP & H(K) & E(PK & data,k)
- A: The Acknowledge packet. It is composed of 3 parts: RDP & H(D)
- K: The Key packet. It is composed of 4 parts: RDP & H(A) & k
- Exchange public keys with the other part and start an encrypted connection.
- If you are the part that initiated the connection then set time1=0 time2=inf and jump to step 11.
- Generate a random key (k) for a symmetric encryption.
- Read data (data) from user.
- Calculate E(data,k) and H(K) (where K is the last K packet received).
- Note down the current time (time1).
- Send a D packet (with the structure described above).
- Wait for an A packet.
- Note down the current time (time2).
- Send a K packet containing the H(A) and the key (k) you used in step 5.
- Wait for a D packet and store the encrypted data (data) it contains.
- Note down the current time (time3).
- If (time2-time1)*2<=(time3-time2) then either there is a MITM or you connection is really unstable.
- Send an A packet containing H(K).
- Wait for a K packet and store the key (k) it contains.
- Show DE(data,k) to the user.
- Jump to step 3.
Saturday, December 15, 2007
Pattern matching
- A program that parses the stdout of the finger command (which is issued for the name of all subdirectories of /home/ ) and then issues an id command for each user on the server of my university. This way I made a 250KB textfile containing usernames, realnames, registration numbers, emails and years of registration; just by organizing information that is normally available by using these two commands. Of course this program in it's present form works correctly only for my university...
- A simple crawler. After making the main crawler part I tried to find something more useful to collect than emails but I came up with nothing so I made it seek email addresses. (but I hate spam :-P )
Saturday, November 17, 2007
Easy Keyboard Layout
There are other keyboard layouts too which try to address some of these problems. One the most famous is the Dvorak keyboard which is probably better than QWERTY.
Anyway, to cut a long story sort, I wrote, before one or two days, a program in Lua that would try to make an optimized layout based on three criteria:
- Letters that appear next to each other often in English text should be far from each other on the keyboard so that the two hands can press them alternatively.
- Characters that appear often with a distance of two characters (like T and E in the word "the") should be close to each other so that that each hand moves as less as it is possible during the alternations.
- The most used character should be close to either the third or the seventh key of the second row (where the D and the J lie on QWERTY keyboards) because that's where I put my middle fingers. :-P
X H N R S T E O K
J U C F Y P B
Today I bought a keyboard (the cheapest I could find) and started switching the positions of the keys to turn it into this:
(the photo is outdated. The P and B should be switched)I still haven't tested it because I bought a PS/2 keyboard while my laptop hasn't got a PS/2 port. So I am waiting to test it to a friend's PC tomorrow.
Of course to use this keyboard you will need to notify the system of the different letter positions. I made a file that can be loaded with xmodmap in Linux to use this keyboard. I will also do the same with Microsoft Keyboard Layout Creator so that Windows will be able to recognize my layout.
I won't upload the program's code yet because I am constantly changing it. If you really want it then just leave a comment asking for it and I will upload it. You might also want to try Kiwi: a program written in C that will create custom keyboard layouts. It looks like a great program but I haven't actually tested it yet.
Amazing fact: Just by changing the position of the letters on a keyboard you can create more than 403,291,461,126,605,635,584,000,000 different layouts.
Sunday, October 14, 2007
FuckBrainFuck v1.7.1
Tuesday, September 18, 2007
Revised FuckBrainfuck Documentation
Monday, September 10, 2007
Infon
Infon is an online multiplayer game. You program the brain of little bugs witch try to survive in an arena by eating food, evolving and multiplying. To play you only need a telnet program. To watch the game though you will need to download the graphical client. Using your telnet program you upload the brain on the server and it executes it. Of course there other games like this like Droidbattles but what makes it different is that it is an online game and it uses my favorite language. Yes, you program the bugs in Lua!
I made a simple brain today which worked quite well. If you know any programming language then it will be easy to learn Lua and try this wonderful game! Click here to go to the website of Infon.
Have fun and if you create a nice brain, challenge me... ;-)
Saturday, August 25, 2007
Lua VS the rest: 1-0
I've started programming (if you can call DOS batch files programs) when I was 11. My first "program" was a wrapper that was supposed to make floppy disk formatting easier by calling format with some arguments and an optional volume label (I think that I had named it KAFmat). What I liked about files? Nothing. I just had no other choice at that time (I had no idea how I could use QBasic, I barely spoke English after all).
After 2 years a family friend gave me an verbatim CD of Visual Basic 6.0. At first I was only playing with putting buttons that did nothing on a form but soon I started making some programs (one year after I made Logicrypt). I continued programming with Visual Basic for about 4 to 5 years while at the same time I tried some other languages like Pascal, Basic, C++. I also tried and learned quite well Plua (=Palm Lua) which I liked a lot because of its great syntax and ease of programming and the fact that it was a complete IDE which I could use on my old Palm Zire.
But, not before a year, I completely uninstalled Windows and Ubuntu took their place. Of course Visual Basic doesn't run on Wine so I had to find an alternative. At that time I was taking Java and C lessons on my university but I never really liked any of these languages because I find them too tricky for daily use: Java is not lightweight at all while C need too much writing for simple programs. I tried Gambas which is like Visual Basic but I stopped using it because it creates executables for Linux only (but is a very good programming environment nevertheless). So I thought that it would be easy to learn Lua since I already knew Plua.
And so I did. I now program mostly in Lua. It is a very powerful language and suitable for daily use. If you have an idea then you simply open a notepad and write some Lua code which will easily run on Linux and Windows. The code is interpreted by the very lightweight and portable Lua virtual machine which fits easily even on a floppy disk. The virtual machine can either run the source code or the compiled bytecode (which you can distribute if you do not want to show the source code of your programs). The virtual machine doesn't support GUIs or sockets but this functionality can be added with the wxlua and the (great) LuaSocket libraries. You may also use LuaGL for OpenGL 3D or 2D Graphics.
I have only published one Lua program until now: FuckBrainfuck
For more info on Lua you may check its entry on wikipedia or go directly to its website by clicking here or at the title of this post.
Tuesday, August 21, 2007
FBF 1.7.0
Brainfuck is a very beautiful esoteric programming language with only 8 commands. You can make any algorithm you can think of on this language but in most cases it is very hard to do so. In Brainfuck you do not have variables, functions, constants and many other convenient features that most programming languages have. You only have an array with byte sized cells (some implementations allow for other sizes too) and a pointer. Initially all cells in the array are equal to 0 and the pointer points to the beginning of the array. Now you can only issue 8 commands that are symbolized by one character and take no arguments: "< > - + , . [ ]". "<" decreases the value of the pointer by one, while ">" increases it. "-" decreases by one the value of the pointed cell on the array, while "+" increases it. "," read a character from the keyboard and stores it in the currently pointed cell, "." prints to the screen the character stored in the currently pointed cell. "[" will jump to the command after the corresponding "]" only if the currently pointed byte equals to 0. "]" will simply jump unconditionally to the corresponding "[". That's all. With this commands you can make any calculation but it will be very hard. You can see Brainfuck as a nice brainteaser; just think of an algorithm and try making it in Brainfuck. For more information on Brainfuck read this article or visit the open directory project for Brainfuck related pages.
FuckBrainfuck (FBF) is a programming language which I made. It compiles directly to Brainfuck code and supports many features found in common programming languages so it makes creating a Brainfuck program easier. Of course you are not actually programming in Brainfuck but you will be able to create very complex Brainfuck programs. The FBF compiler is written in Lua (which is one of my favorite programming laguages) and will run on both Linux and Windows. In the zipfile you can find an extensive documentation, the source code of the compiler (GNU/GPL version 3 license), Lua interpreters for Windows and Linux and some example programs in FBF.
Here is a small program written in FBF:
#DIM char
READ char
UNEQ char '.'
IFNOTEQ char 'z'
INC char 1
END
IFEQ char 'z'
SET char 'a'
END
PRINT char
READ char
END
You can download FuckBrainfuck by clicking here.
Popular Posts
-
This article probably also applies to: VGN-FZ38M Yeap I bought it. I though that since it had an NVidia card and it was Centrino based etc i...
-
It seems that the most popular article on this blog is " How to make portable applications " written back in 2007. Reading it aga...
-
Don't read this article. I've written a much better one which you can find here . The purpose of this (late-night) post is to sho...