25
\$\begingroup\$

The goal of radiation hardening is to make a program still work after you remove a byte. However, what if a skilled competitor is purposefully trying to mess with you?

In this game, each bot will take turns removing one character from the next bot down the line. If the bot fails to produce valid output next turn you gain one point. The bot with the most points at the end wins. Simulation will be run several times with different orders to reduce randomness. Final score will be the median of each iteration.

If a program reaches 0 bytes in length it will always be eliminated, regardless of if it produces valid output or not. Otherwise it would be unbeatable.

Note the goal of the game is to get kills, not necessarily to survive the longest. You can be the last one living but if you haven't eliminated anyone you can still lose. Of course, it's impossible to gain any more points after being eliminated.

IO format

Since I want many languages to be able to compete, I will need to enforce strict IO rules to make it possible for me to run each program. Thus, IO will be purely using STDIN and STDOUT.

The input of a program is the full source code of the next program, piped over STDIN.

The output is the 0-based index of the byte you are trying to remove, then a space, then the byte value. For example if the victim was "abc" a valid output would be "1 b". A optional trailing newline is allowed but any other extra output is invalid. If the character after the space does not match the victim's byte at that position the output will be considered invalid.

Language selection

This will be a multi language KOTH. Languages must be free to participate and able to run inside a Ubuntu Linux Docker container. Please include a installation script and a script that can run your program. Your script should also install libraries or packages include a script that installs them.

Languages purposefully designed to beat this challenge are disqualified.

Programing languages must be capable of reading the code form a file, taking input from STDIN, and outputing to STDOUT, just because they are hard for me to run reliably otherwise. I may make exceptions to this rule, mostly if you have sufficiently detailed setup and run instructions. Bots posted before this edit are exempt.

Example post

Example Bot - C

Code:

///////////////////////////////////////////////////////////////////////////////////
k,j;main(i){for(j=getchar();i<255&&k>=0;i++){k=getchar();if(!((k&168)^'(')){printf("%d %c",i,k);exit(0);}}printf("0 %c",j);}

Base 64 (Only needed if you have unprintable characters or want to use a SBCS)

Ly8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8KayxqO21haW4oaSl7Zm9yKGo9Z2V0Y2hhcigpO2k8MjU1JiZrPj0wO2krKyl7az1nZXRjaGFyKCk7aWYoISgoayYxNjgpXicoJykpe3ByaW50ZigiJWQgJWMiLGksayk7ZXhpdCgwKTt9fXByaW50ZigiMCAlYyIsaik7fQ==

Installation:

apt-get install gcc -y

How to run: (Include any command line arguments your program needs here)

mv code code.c
gcc code.c -o code
./code

Explanation

Removes the first instance of ['(', ')', ',', '-', 'h', 'i', 'l', 'm'] from it's target. If none is found remove the first character.

Controller

https://codeberg.org/Mousetail/Radiation-Hardened-Koth

Chat room

Leaderboard (Last updated (FINAL UPDATE) 29-08-2022 6:48 UTC)

window.location.href = "https://mousetail.github.io/koth-leaderboard-pages/results.html"

You are free to edit and submit more bots, but if you want to appear on the leader-board you need to run the controller yourself. I'd be happy to accept pull requests to the ranking repo.

Other rules

  1. Program length can be up to 256 bytes
  2. No opening files, networking, or any other IO. Writing to STDERR is ok.
  3. It's ok to try to crash programs by including NULL bytes and invalid unicode etc. in your source code.
  4. Programs must take <1s to complete. Taking longer than 2s is considered invalid output, though try to keep is less than 1s to account for uncertainty.
  5. It's ok to submit multiple bots as long as they are not specifically designed to support each-other.
  6. Command line arguments are allowed, but no cheating by putting the "real" code in the command line arguments.
  7. Any negative votes bots will not participate until they gain more votes. This is intended to help moderate the points above when subjective.
\$\endgroup\$
30
  • 3
    \$\begingroup\$ @Jonah each round each bot has 1 less character, so it will be different \$\endgroup\$ Commented Aug 18, 2022 at 16:26
  • 4
    \$\begingroup\$ The positive votes idea is very clever, I'm surprised no one's thought of it \$\endgroup\$
    – emanresu A
    Commented Aug 19, 2022 at 9:22
  • 2
    \$\begingroup\$ Red = more frequent, blue = less frequent. If a letter is red in the vulnerable (top) part it means this letter is removed a lot. The bottom part it's how often that letter was the last removed before the bot died @thejonymyster \$\endgroup\$ Commented Aug 19, 2022 at 17:32
  • 2
    \$\begingroup\$ are programs ever run against themselves? if not, id be curious to see what would happen lol but obviously it isn't super pertinent to the challenge if it isn't already part of it \$\endgroup\$ Commented Aug 22, 2022 at 22:14
  • 5
    \$\begingroup\$ Happy Birthday! \$\endgroup\$ Commented Aug 26, 2022 at 9:57

25 Answers 25

12
\$\begingroup\$

A Pear Tree, Distributed Damage

A Pear Tree isn't actually that good at this challenge as languages go – its general efficiency at is because it's good at detecting damage, but it has no real way to repair it, so the best you can do is to write the same program several times in a row and hope that at least one copy stays undamaged.

Nonetheless, I'm entering an answer by popular demand :-)

This program contains unprintable characters (thus no TIO link). However, it looks something like this (except that ��\n are actually three non-newline nonprintable characters); see below for a hex dump:

l=length;(.618*l)=~/\..*/;i=int($&*l);$_=i." ".substr$_,i,1#-��
l=length;(.618*l)=~/\..*/;i=int($&*l);$_=i." ".substr$_,i,1#-��
l=length;(.618*l)=~/\..*/;i=int($&*l);$_=i." ".substr$_,i,1#-��
l=length;(.618*l)=~/\..*/;i=int($&*l);$_=i." ".substr$_,i,1#-��

Hexdump

This is the actual submission, as an xxd reversible hex dump.

00000000: 6c3d 6c65 6e67 7468 3b28 2e36 3138 2a6c  l=length;(.618*l
00000010: 293d 7e2f 5c2e 2e2a 2f3b 693d 696e 7428  )=~/\..*/;i=int(
00000020: 2426 2a6c 293b 245f 3d69 2e22 2022 2e73  $&*l);$_=i." ".s
00000030: 7562 7374 7224 5f2c 692c 3123 2db4 9616  ubstr$_,i,1#-...
00000040: 6c3d 6c65 6e67 7468 3b28 2e36 3138 2a6c  l=length;(.618*l
00000050: 293d 7e2f 5c2e 2e2a 2f3b 693d 696e 7428  )=~/\..*/;i=int(
00000060: 2426 2a6c 293b 245f 3d69 2e22 2022 2e73  $&*l);$_=i." ".s
00000070: 7562 7374 7224 5f2c 692c 3123 2db4 9616  ubstr$_,i,1#-...
00000080: 6c3d 6c65 6e67 7468 3b28 2e36 3138 2a6c  l=length;(.618*l
00000090: 293d 7e2f 5c2e 2e2a 2f3b 693d 696e 7428  )=~/\..*/;i=int(
000000a0: 2426 2a6c 293b 245f 3d69 2e22 2022 2e73  $&*l);$_=i." ".s
000000b0: 7562 7374 7224 5f2c 692c 3123 2db4 9616  ubstr$_,i,1#-...
000000c0: 6c3d 6c65 6e67 7468 3b28 2e36 3138 2a6c  l=length;(.618*l
000000d0: 293d 7e2f 5c2e 2e2a 2f3b 693d 696e 7428  )=~/\..*/;i=int(
000000e0: 2426 2a6c 293b 245f 3d69 2e22 2022 2e73  $&*l);$_=i." ".s
000000f0: 7562 7374 7224 5f2c 692c 3123 2db4 9616  ubstr$_,i,1#-...

Strategy/algorithm

Most of the submissions to this challenge so far use one of two hardening strategies: either the "core and chaff" strategy, in which only a part of the program has any meaning and the rest is just junk to distract the opponents, or a more traditional approach which has redundant copies of all the important data and will attempt to find and use an undamaged copy.

Distributed Damage works by trying to spread its deletions evenly over the target program; against "core and chaff" this is probably a bit more likely to hit the core than choosing randomly (the first shot is no better than random, but subsequent shots avoid areas that didn't kill the opponent, and are thus more likely to aim for places that could); and against "redundant copies" this is more likely than random to damage every copy and thus prevent the program being repaired.

In order to work out where the program has previously attacked, the attack position is based entirely on the length of the target program (thus, if the program is, e.g., 219 bytes long, it's possible to determine where the previous attack was via knowledge of which byte is attacked in a 220-byte program). The length of the target program is multiplied by (an approximation of) the golden ratio, and the fractional portion of the result is used to give the target to attack – this naturally leads to the attack positions being spread in a distributed way across the target program.

This algorithm was inspired by nature – many plants use the same algorithm to, e.g., place their leaves in such a way that they aren't blocking each others' view of the sun, because it makes it possible to distribute locations approximately evenly even if you don't know in advance how many of them there will be, and the calculation is very simple.

The radiation hardening isn't anything special, just a "redundant copies" approach, with four copies of the 64-byte program (256 bytes in all). The last four bytes of each copy are a checksum; A Pear Tree refuses to run programs unless they have a section that checksums correctly, and will run that section first (thus, if any of the four copies is undamaged, that copy runs). Unfortunately, this means that there's no way to continue if all four copies get damaged, because the program won't parse at all. (It might be possible to put a secondary checksum around the portion from i= onwards so that at least the interpreter would have something to run if every copy got damaged, but that would be unlikely to get extra kills, and I ran out of bytes so there's no room for extra checksums.)

I'm hoping that this answer will do particularly well defending against, and decently at attacking, "tiny core" programs that aim purely for survival rather than kills, and don't have any algorithm for choosing targets – such programs nearly always choose targets near the start, and this program is unlikely to stop working unless all four quarters of it get hit (in particular, the last quarter has to get hit). Programs like Almost Rare that are designed to detect programs consisting of redundant copies and damage them all in the same way are likely to beat this very quickly, though.

Installation

Install perl if necessary (it's installed by default on Ubuntu).

Install Digest::CRC (Ubuntu package libdigest-crc-perl).

Download http://nethack4.org/esolangs/apeartree-v2.pl.

To run the program, use perl apeartree-v2.pl programfilename. A Pear Tree reads in binary (rather than Unicode) by default, so there's no need to worry about changing the default encoding.

Explanation

The interpreter for A Pear Tree will rotate an undamaged portion of the program to the start (determined by looking for a valid checksum). If it can't, that's a syntax/checksum error (a partridge).

If it finds an undamaged portion, that portion will do the following:

                       # {implicit input, reading all of stdin into $_}
l=length;              # assign length of {the input} to `l`, as shorthand
(.618*l)               # multiply the length by 0.618
        =~/\..*/;      # regex, matching from `.` to end of string
                         # i.e. this matches the fractional portion
i=int(                 # assign to `i` the value, truncated to integer, of
      $&*l);             # the string matched by the regex, times `l`
$_=i                   # assign the following to $_: `i`, 
    ." ".                # concatenated with " ", concatenated with
         substr$_,       # the substring of the input
                  i,1      # starting at `i`, with length 1
#                      # comment to newline (i.e. end; there is no newline)
                       # {implicit output of $_}

It's a bit surprising that regexing numbers (i.e. converting them to strings and looking for the decimal point) is the tersest way to extract the fractional portion, but I haven't found anything better.

One change for golfiness, necessary to get this down to 64 bytes: the golden ratio is approximately equal to 1.618, not 0.618, but because we're taking the fractional portion of the result it doesn't matter what the integer portion of the value we're multiplying by is.

\$\endgroup\$
6
  • \$\begingroup\$ I was hoping for a pear tree answer \$\endgroup\$ Commented Aug 19, 2022 at 18:43
  • \$\begingroup\$ I'm getting this error, it's probably a easy fix but I don't know enough about perl: an't locate Digest/CRC.pm in @INC (you may need to install the Digest::CRC module) (@INC contains: /etc/perl /usr/local/lib/x86_64-linux-gnu/perl/5.34.0 /usr/local/share/perl/5.34.0 /usr/lib/x86_64-linux-gnu/perl5/5.34 /usr/share/perl5 /usr/lib/x86_64-linux-gnu/perl-base /usr/lib/x86_64-linux-gnu/perl/5.34 /usr/share/perl/5.34 /usr/local/lib/site_perl) at apeartree-v2.pl line 76. BEGIN failed--compilation aborted at apeartree-v2.pl line 76. \$\endgroup\$ Commented Aug 19, 2022 at 19:11
  • 1
    \$\begingroup\$ @mousetail: Looks like I was mistaken about which packages shipped with Perl. I've added installation instructions for Digest::CRC to the post – fortunately it's packaged on Ubuntu. \$\endgroup\$
    – ais523
    Commented Aug 19, 2022 at 19:34
  • 1
    \$\begingroup\$ "A Pear Tree isn't actually that good at this challenge as languages go". And yet, this bot is completely dominating in 1st place. \$\endgroup\$
    – Aiden Chow
    Commented Aug 26, 2022 at 2:04
  • \$\begingroup\$ Does modulo 1 not work to get the fractional part? \$\endgroup\$
    – emanresu A
    Commented Aug 26, 2022 at 22:57
11
\$\begingroup\$

Jelly with LANG=C.ISO-8859-1, The Burrowing Wheel

⁸

¹¹»¹«
¹
ṛ⁸ẈMị⁸ẈMị⁸ẈMị⁸ẈMị⁸ẈMị⁸ẈMị⁸ẈMị⁸ẈMị

¹

¹ ḷ

¹« »


 ¹




«a

oao

«»«»« »«»«» « »« »

ḷ¹¹ ¹

ṛ
“““““““““““““““““““““““⁹ƈ€µTịµJḟⱮ`ịṙṢṪ€ɗ€JŒgẈNMX©ịṭ®_1¤KȮị““““⁹ƈ€µTịµJḟⱮ`ịṙṢṪ€ɗ€JŒgẈNMX©ịṭ®_1¤KȮị““““⁹ƈ€µTịµJḟⱮ`ịṙṢṪ€ɗ€JŒgẈNMX©ịṭ®_1¤KȮị”µµẈMị¹¹¹⁺VVV⁺

Try it online!

Exits by crashing, putting junk on standard error, but standard output should be correct.

I don't know how fast the OP's computer is, and it matters for this submission – this program takes around 0.5-0.6 seconds to run on my computer and on TIO, so may go over the time limit if the OP's computer is significantly slower.

Jelly uses a custom code page, so here's a hexdump (xxd format) of what this looks like after encoding:

00000000: 887f 7f81 81fb 81fa 7f81 7fde 88bb 4dd8  ..............M.
00000010: 88bb 4dd8 88bb 4dd8 88bb 4dd8 88bb 4dd8  ..M...M...M...M.
00000020: 88bb 4dd8 88bb 4dd8 88bb 4dd8 7f7f 817f  ..M...M...M.....
00000030: 7f81 20da 7f7f 81fa 20fb 7f7f 7f20 817f  .. ..... .... ..
00000040: 7f7f 7f7f fa61 7f7f 6f61 6f7f 7ffa fbfa  .....a..oao.....
00000050: fbfa 20fb fafb fafb 20fa 20fb fa20 fb7f  .. ..... . .. ..
00000060: 7fda 8181 2081 7f7f de7f fefe fefe fefe  .... ...........
00000070: fefe fefe fefe fefe fefe fefe fefe fefe  ................
00000080: fe89 9c0c 0954 d809 4aeb 9560 d8f3 b7ce  .....T..J..`....
00000090: 0c9d 0c4a 1367 bb4e 4d58 06d8 e008 5f31  ...J.g.NMX...._1
000000a0: 034b cad8 fefe fefe 899c 0c09 54d8 094a  .K..........T..J
000000b0: eb95 60d8 f3b7 ce0c 9d0c 4a13 67bb 4e4d  ..`.......J.g.NM
000000c0: 5806 d8e0 085f 3103 4bca d8fe fefe fe89  X...._1.K.......
000000d0: 9c0c 0954 d809 4aeb 9560 d8f3 b7ce 0c9d  ...T..J..`......
000000e0: 0c4a 1367 bb4e 4d58 06d8 e008 5f31 034b  .J.g.NMX...._1.K
000000f0: cad8 ff09 09bb 4dd8 8181 818a 5656 568a  ......M.....VVV.

Strategy/algorithm

This program checks to see which of the various possible irradiated versions are shortest when compressed, and chooses a byte to delete that produces one of those most-compressible targets.

The idea behind this algorithm is:

  • Repetitive chaff (like ///////////////) compresses very well, so the algorithm avoids damaging it (which is generally a useless thing to do, programs with long repeating runs of characters rarely care about the exact number);
  • Redundant parts of a program (that could survive damage) are often repeated multiple times; the algorithm will tend not to damage those because damaging one copy will make the program less compressible, rather than more;
  • If one redundant part of a program does have to be damaged (e.g. because the entire program is made of them), the algorithm will then tend to try to damage other parts in the same way;
  • If a program is formed of some completely random chaff, plus some meaningful code, the algorithm is more likely to target the meaningful code; this is because meaningful code often contains near-repeats (and the algorithm will tend to make them more exact repeats, thus breaking the code), whereas completely random chaff doesn't, and so there's more of a compressibility gain to attacking the meaningful code than the chaff.

Jelly doesn't have compression (as opposed to decompression) builtins, so in order to work out what the most compressible irradiated versions are, I had to write my own compressor. The idea is to take a Burrows–Wheeler transform of the input (this is the same general technique that bzip2 uses) – take all cyclic permutations of the irradiated program, sort them, and look at the last bytes of each permutation in their sorted order. If a substring appears repeatedly, then imagine the cyclic permutations that split that substring between the start and end of the string; for each copy, the portion that's at the start will be the same, so they'll sort next to each other, and thus the portion at the end (which is also the same) will also sort next to each other, creating runs of the same value in the list of the last bytes.

Burrows–Wheeler-based compression works by run-length-encoding that list of the last bytes, but to work out which irradiated program is the most compressible, there's no actual need to go through with the rest of the compression; it's possible simply to count how many runs there are. This submission does that, looks for the irradiated version with the fewest runs, and outputs whichever byte will produce that particular irradiated version.

Often there'll be more than one possibility, in which case this program picks at random.

The TIO! link above shows this program attacking NÜL tränslätör – there are only two target bytes it will attack, the r of tr and the first " of printf "%d ". Both of these bytes are fatal to the target, so the algorithm is making good choices there. (The attack on the printf quotes is because the other printfs don't use quotes, so it's trying to make all the printfs the same. I'm not totally sure what causes the attack on tr, but suspect that it's related to most uses of r being in an expression (r+1) or to spell printf – the algorithm has thus correctly identified meaningful code that doesn't have a redundant copy elsewhere in the program.)

Installation

There's no change from my other Jelly answer. So that this answer is complete, I'm repeating the instructions here:

  • Install python3 and pip3 (on Ubuntu, python3 and python3-pip); also git if you haven't already.
  • You will need to run from an account with a real home directory – if your container is so stripped down that it doesn't have one, create one with, e.g.,
    mkdir ./home
    export HOME=$(readlink -f ./home)
    
  • Install Jelly using the instructions at https://github.com/DennisMitchell/jellylanguage:
    git clone -q https://github.com/DennisMitchell/jellylanguage.git
    cd jellylanguage
    pip3 install --upgrade --user .
    

To run programs: LANG=C.ISO-8859-1 ~/.local/bin/jelly f programfilename, where programfilename is the name of the program you're running. Standard input can be piped in or redirected from a file, as normal.

Explanation

Radiation hardening

The first half of the program is just chaff, intended to create false-positives for most programs that try to aim their attacks, and to take ages to chew through for programs that scan from the start of the program in sequence – it's possible to delete the whole thing and nothing will change. Almost everything here is a) the empty string, or b) a function that always returns one of its arguments literally, so even if some of this code somehow ends up running, it won't do anything. However, Jelly starts execution from the start of the last line of the program, so you need to start deleting newlines in order to even get the code to run.

The radiation hardening is done using three copies of the core program logic – the longest will be run. As an improvement over my previous Jelly submission, I separated them with multiple marks (which in this context are kind-of like commas) – in order to cause two copies to be combined and become longest, you need to delete all the marks in that section. Most likely, it's easier to disrupt the program by damaging all three copies separately than by causing two to be combined.

I didn't radiation-harden the code that selects an appropriate copy to run, but it's only four bytes long and one is hardened (as ¹ is a no-op in most contexts, making it safe to add redundant copies of it), so hard to hit unless aiming at this program in particular:

ẈMị¹
Ẉ     Take the length of each program in the list
 M    Find the indexes of the maximums (i.e. longest programs)
  ị   Index back into
   ¹    the original list

This gives us a list of the longest programs, which is subsequently evaluated using V. The programs actually get concatenated together by V, so there's an intentional crash to exit before the program runs a second time and produces output again.

Target selection

⁹ƈ€µTịµJḟⱮ`ịṙṢṪ€ɗ€JŒgẈNMX©ịṭ®_1¤KȮị
⁹ €                                   Form a list of 256 elements
 ƈ                                      read from standard input
    T                                 Find the truthy elements
   µ i                                  and select them
       J                              Take the indexes of the elements
         Ɱ`                           For each index
        ḟ                               produce a list of indexes that omits it
      µ    ị                          Index back into the input
                ɗ€                    On each of those lists, do:
            ṙ     J                     rotate it by all possible amounts
             Ṣ                          sort the rotated lists
              Ṫ                         take the last element
               €                          of each of the rotated lists
                   Œg                 Group consecutive equals in {each list}
                     Ẉ                Take the length of each list
                      NM              Find all indexes of the minimum value
                        X             Pick one at random
                         ©              and store it in ®
                          ị           Take the {input character} at that index
                           ṭ          Prepend
                            ®_1¤        ® - 1
                                K     separated by a space
                                 Ȯ    Output the result
                                  ị   Index {using a string}, crashes
            

The ṙ … J is actually slightly buggy – the J is measuring against the length of the original program, not the length of the irradiated version, so one of the rotated lists ends up being added twice. However, this ends up having no impact on the results, because the two identical lists will sort next to each other, and their last elements are the same, thus end up in the same run of consecutive equals and don't change the total number of runs.

\$\endgroup\$
8
\$\begingroup\$

Jelly with LANG=C.ISO-8859-1, Almost Rare

““““““⁹ƈ€µTịµṢœ-QŒrṪÐṂFX©⁸ẹX_1⁶®⁸ị`“⁹ƈ€µTịµṢœ-QŒrṪÐṂFX©⁸ẹX_1⁶®⁸ị`“⁹ƈ€µTịµṢœ-QŒrṪÐṂFX©⁸ẹX_1⁶®⁸ị`“⁹ƈ€µTịµṢœ-QŒrṪÐṂFX©⁸ẹX_1⁶®⁸ị`“⁹ƈ€µTịµṢœ-QŒrṪÐṂFX©⁸ẹX_1⁶®⁸ị`“⁹ƈ€µTịµṢœ-QŒrṪÐṂFX©⁸ẹX_1⁶®⁸ị`“⁹ƈ€µTịµṢœ-QŒrṪÐṂFX©⁸ẹX_1⁶®⁸ị`”¹¹LÐṀoo¹¹VVV”¹¹LÐṀoo¹¹VVV”¹¹LÐṀo¹¹VVV

Try it online!

Hexdump (xxd reversible hexdump format, decode with xxd -r):

00000000: fefe fefe fefe 899c 0c09 54d8 09b7 1e2d  ..........T....-
00000010: 5113 72ce 0fb3 4658 0688 d658 5f31 8608  Q.r...FX...X_1..
00000020: 88d8 60fe 899c 0c09 54d8 09b7 1e2d 5113  ..`.....T....-Q.
00000030: 72ce 0fb3 4658 0688 d658 5f31 8608 88d8  r...FX...X_1....
00000040: 60fe 899c 0c09 54d8 09b7 1e2d 5113 72ce  `.....T....-Q.r.
00000050: 0fb3 4658 0688 d658 5f31 8608 88d8 60fe  ..FX...X_1....`.
00000060: 899c 0c09 54d8 09b7 1e2d 5113 72ce 0fb3  ....T....-Q.r...
00000070: 4658 0688 d658 5f31 8608 88d8 60fe 899c  FX...X_1....`...
00000080: 0c09 54d8 09b7 1e2d 5113 72ce 0fb3 4658  ..T....-Q.r...FX
00000090: 0688 d658 5f31 8608 88d8 60fe 899c 0c09  ...X_1....`.....
000000a0: 54d8 09b7 1e2d 5113 72ce 0fb3 4658 0688  T....-Q.r...FX..
000000b0: d658 5f31 8608 88d8 60fe 899c 0c09 54d8  .X_1....`.....T.
000000c0: 09b7 1e2d 5113 72ce 0fb3 4658 0688 d658  ...-Q.r...FX...X
000000d0: 5f31 8608 88d8 60ff 8181 4c0f c86f 6f81  _1....`...L..oo.
000000e0: 8156 5656 ff81 814c 0fc8 6f6f 8181 5656  .VVV...L..oo..VV
000000f0: 56ff 8181 4c0f c86f 8181 5656 56         V...L..o..VVV

Exits via crashing, so a lot of junk goes to stderr and must be ignored, but stdout should be correct.

Strategy/algorithm

This program looks for characters in the input program that appear more than once, but under that restriction, as few times as possible. (So the ideal is to find a character that appears twice.) It picks one of those characters at random, picks a random occurrence of it, and chooses that.

The program is (unless I've missed something) radiation-hardened against any single deletion, and can frequently withstand quite a lot of random deletions (although there are ways to make it fail with only two deletions, it takes some understanding to realise what needs to be deleted).

Unfortunately, Jelly by default has no byte-level I/O at all – any attempt to read invalid UTF-8 (except by shelling out to Python) will cause a crash in the read routines. This is fixable by explicitly disabling Unicode handling when running the program, using LANG=C.ISO-8859-1 as a prefix to the command. (This may produce warnings because your OS probably doesn't have non-Unicode locale data files installed nowadays, but Python should still be able to run under these conditions.) Note that the similar LC_ALL doesn't work – using this triggers a special case in Jelly which produces output encoded in Jelly's codepage, rather than the specified encoding.

Installation

  • Install python3 and pip3 (on Ubuntu, python3 and python3-pip)
  • You will need to run from an account with a real home directory – if your container is so stripped down that it doesn't have one, create one with, e.g.,
    mkdir ./home
    export HOME=$(readlink -f ./home)
    
  • Install Jelly using the instructions at https://github.com/DennisMitchell/jellylanguage:
    git clone -q https://github.com/DennisMitchell/jellylanguage.git
    cd jellylanguage
    pip3 install --upgrade --user .
    

To run programs: LANG=C.ISO-8859-1 ~/.local/bin/jelly f programfilename, where programfilename is the name of the program you're running. Standard input can be piped in or redirected from a file, as normal.

Explanation

The program that actually does the work is:

⁹ƈ€µTịµṢœ-QŒrṪÐṂFX©⁸ẹX_1⁶®⁸ị`
 ƈ€                            Read     characters from standard input
⁹                                   256
   µTị                         Remove falsey characters (i.e. past EOF)
      µ                        Assign to ⁸
       Ṣ                       Sort the characters,
        œ-                       and remove
          Q                        one occurence of each character.
           Œr                  Run-length encode,
              ÐṂ                 and take the (char, length) pairs with minimal
             Ṫ                   last component (i.e. length)
                F              Convert into a list of chars, no lengths
                 X             Pick one at random
                  ©              and assign it to ©
                   ⁸ẹ          Find all locations of it within ⁸
                     X         Pick one at random
                      _1       Subtract 1 (to use 0-based indexing)
                        ⁶      {Output the location}, and a space,
                         ®     and ®
                          ⁸ị`  Index the input into itself (i.e. crash)

This gets repeated many times as elements of a giant list literal, which is then decoded:

¹¹LÐṀoo¹¹VVV
¹¹             no-ops, prevent a crash if the `L` is deleted
   ÐṀ          take the list elements with the maximum
  L              length
     oo¹¹      logical-OR with the list literal (a no-op, usually)
         V     evaluate (running the program and crashing)
          VV   spare Vs in case one gets deleted

The various deletions from this will generally either do nothing, or represent a program that concatenates the entire list literal into a single program and runs it (so in effect it ends up running the first copy of the program rather than the longest, i.e. least mutated, copy) – it's carefully structured to prevent syntax errors appearing as a result of deletions, e.g. Ðo is a valid command ("apply to odd elements") and thus won't cause a crash before the evaluation runs.

The decoder is also repeated, but that's mostly irrelevant / chaff to confuse other programs about what we're doing, and matters only if the that closes the list literal gets deleted (in that case, the second copy of the decoder is used instead).

\$\endgroup\$
10
  • \$\begingroup\$ This throws a error: gist.github.com/mousetail/6e6849bb0b858808816bedab71610e83 \$\endgroup\$ Commented Aug 18, 2022 at 18:18
  • \$\begingroup\$ Outputted b'0 061 w\n' which isn't valid, should be at most exactly one space and the literal character value of what you deleted \$\endgroup\$ Commented Aug 18, 2022 at 18:23
  • 1
    \$\begingroup\$ The error is expected – it's the easiest way to end the program and stop it producing more output. I have noticed a mistake in the program (it's using 1-based rather than 0-based indexes, I'll have to fix that), but I'm surprised that the output you're getting is so different from the intended output – I wonder if something went wrong with encoding? (Perhaps you copy-and-pasted the program rather than using the hexdump? Jelly doesn't use UTF-8, so a copy-and-pasted program will be misencoded.) \$\endgroup\$
    – ais523
    Commented Aug 18, 2022 at 20:30
  • \$\begingroup\$ I used the hexdump in the way you suggested, using xxd -r | base64. You can double check in the base64 version I used is correct here: codeberg.org/Mousetail/Radiation-Hardened-Koth/src/branch/main/… \$\endgroup\$ Commented Aug 18, 2022 at 20:32
  • \$\begingroup\$ I've edited the program to fix the 0- versus 1-based indexing error, but both versions of the program are working fine for me on TIO (on a simple test input) and locally (I tested on your example bot above), so I'm surprised that it isn't working in your environment – I can't think of anything that might have broken it. (The base64 looks correct.) \$\endgroup\$
    – ais523
    Commented Aug 18, 2022 at 20:41
6
\$\begingroup\$

Parentheses Killer - PARI/GP

Code

/*in"(n1fo]e)itr(s-),)i=nj1sn)io#r]nnsrt()*/print(-1+i=if(a=[i|i<-[1..#s=Vec(strjoin(readstr("/dev/stdin"/*1e=a1i= o""sr==s,fia")[aVtid(ir.iddsr")i*/)))],s[i]=="("],a[random(#a)+1],random(#s)+1)" "s[i])\\)i]so)(smirc/=/o"r,tr+)1++[")=s"[id)nos]

Installation

Just install the pari-gp package on Ubuntu.

apt install -y pari-gp

How to run

Assume that the filename is a.gp:

gp -fq a.gp

Explanation

Removes a random ( if there is one. Otherwise removes a random character.

Some random comments are added to make it more resistant to radiation.

PARI/GP doesn't have a built-in to read a string from STDIN, so I have to use readstr("/dev/stdin"). This only works on unix-like systems.

\$\endgroup\$
3
  • \$\begingroup\$ Nice answer, seems to never last longer than a single round though \$\endgroup\$ Commented Aug 18, 2022 at 12:53
  • \$\begingroup\$ @mousetail I modified the code and added some comments. I hope this is allowed since there isn't any other answer yet. \$\endgroup\$
    – alephalpha
    Commented Aug 18, 2022 at 13:42
  • \$\begingroup\$ Yes totally fine. You are on the board now with 1 total kill. \$\endgroup\$ Commented Aug 18, 2022 at 13:49
6
\$\begingroup\$

Ghost, Whitespace

A bot that will remove white-space from other bots, while itself being made of only whitespace.

Code:

    
v 
 un 
                0r      
e_
    
    
 
    
                of 
 e
                
r\ 
 }           
y       
        
g 
 i          
e       nc
        
te 
 (s          
no      
        
l 

an      
i(     \w
 
  

        
n 

la 

hr    
t" 
    t(
 
    
l
    
'a 
    .   
             
l=  
  ) 
  ie


uin'o{tts)fa:

Base64:

ICAgIAp2IAogdW4JCgkgCQkJMHIgICAJCmVfCiAgICAKICAgIAogCiAJCgkgCQkJb2YgCiBlCgkJCQkKclwgCiB9ICAgCSAgICAgCnkJICAJCgkgIAkKZyAKIGkgICAJICAJCmUJICAJbmMKCSAgCQp0ZSAKIChzICAgCSAJIApubwkgIAkKCSAgCQpsIAoKYW4gICAJCmkoCSAgIFx3CiAKICAKCiAgCQkKbiAKCmxhIAoKaHIgICAgCnQiIAoJdCgKIAogCQpsCiAgIAkKJ2EgCgkuCQogCSAgIAkgICAgIApsPQkKICApCQogIGllCgoKdWluJ297dHRzKWZhOg==

Disassembled

PUSH 0
DUP
READC ; backup character
RETREIVE
PUSH 1

; START MAIN LOOP
MARK 0  ; main loop


PUSH 0
DUP
READC
; SPACE PART
RETREIVE
DUP
JLT -1
DUP
PUSH ' '
SUB
JEQ 1

; TAB PART
DUP
PUSH 9 ; tab
SUB
JEQ 1
; END TAB PART

; LINE BREAK PART
DUP
PUSH 10
SUB
JEQ 1
POP
; END LINE BREAK PART

PUSH 1
ADD
JMP 0

MARK -1

POP
POP
PUSH 0
SWAP
JMP 1

MARK 1

SWAP
WRITEN
PUSH ' '
WRITEC
WRITEC
EXIT

Setup

I spent a long time finding a whitespace interpreter that allowed me to detect EOF and write logic for it.

apt-get install -y make
apt-get install -y g++
if [[ ! -d pavelshub-cpp ]]
then
    git clone https://github.com/wspace/pavelshub-cpp.git
    cd pavelshub-cpp
    make
    cd ..
fi

Run:

./pavelshub-cpp/wspace code
\$\endgroup\$
1
  • 2
    \$\begingroup\$ Ouch. This one will be hard to compete against \$\endgroup\$
    – Seggan
    Commented Aug 19, 2022 at 14:35
6
\$\begingroup\$

Befunge-98 (FBBI), Comment Skipper

Note: in the actual submission, the rs are replaced with nonprintable or invalid-UTF-8 bytes; see the hexdump below. I've used r to represent them in this post and in the TIO link, because all unimplemented commands in Befunge-98 have identical semantics to r.

50050050050050050050050050050050050050050xxxxxnnnnn#####;;;;;~~~~~:::::~~~~~-----!!!!!!!!!!;;;;;@@@@@,,,,,.....;;;;;#####\\\\\kkkkkrrrrr'''''#####-----:::::ccccc-----*****#####rrrrr!!!!!;;;;;#####-----aaaaa~~~~~+++++;;;;;#####11111kkkkkrrrrr22222+++++;;;;;

Try it online!

Hexdump

This is the actual submission, as an xxd reversible hex dump.

00000000: 3530 3035 3030 3530 3035 3030 3530 3035  5005005005005005
00000010: 3030 3530 3035 3030 3530 3035 3030 3530  0050050050050050
00000020: 3035 3030 3530 3035 3078 7878 7878 6e6e  050050050xxxxxnn
00000030: 6e6e 6e23 2323 2323 3b3b 3b3b 3b7e 7e7e  nnn#####;;;;;~~~
00000040: 7e7e 3a3a 3a3a 3a7e 7e7e 7e7e 2d2d 2d2d  ~~:::::~~~~~----
00000050: 2d21 2121 2121 2121 2121 213b 3b3b 3b3b  -!!!!!!!!!!;;;;;
00000060: 4040 4040 402c 2c2c 2c2c 2e2e 2e2e 2e3b  @@@@@,,,,,.....;
00000070: 3b3b 3b3b 2323 2323 235c 5c5c 5c5c 6b6b  ;;;;#####\\\\\kk
00000080: 6b6b 6bff ffff ffff 2727 2727 2723 2323  kkk.....'''''###
00000090: 2323 2d2d 2d2d 2d3a 3a3a 3a3a 6363 6363  ##-----:::::cccc
000000a0: 632d 2d2d 2d2d 2a2a 2a2a 2a23 2323 2323  c-----*****#####
000000b0: 0000 0000 0021 2121 2121 3b3b 3b3b 3b23  .....!!!!!;;;;;#
000000c0: 2323 2323 2d2d 2d2d 2d61 6161 6161 7e7e  ####-----aaaaa~~
000000d0: 7e7e 7e2b 2b2b 2b2b 3b3b 3b3b 3b23 2323  ~~~+++++;;;;;###
000000e0: 2323 3131 3131 316b 6b6b 6b6b c1c1 c1c1  ##11111kkkkk....
000000f0: c132 3232 3232 2b2b 2b2b 2b3b 3b3b 3b3b  .22222+++++;;;;;

Strategy/algorithm

Looks at characters in pairs from the start of the program. If they're:

  • not equal, deletes the first character of the pair;
  • equal but not // nor ##, moves onto the next pair;
  • equal and // or ##, moves onto the next line.

The idea is that if programs in practical languages are using "hide where my code is" techniques rather than proper radiation-hardening techniques, we should be able to find the start of the real code. If the program starts with a large number of repetitive no-ops, we can skip over those because every consecutive pair in a long string of repeated characters is equal; if it starts with a comment marker consisting of repeated characters, that's in most practical languages probably going to be ////… \n or #####… \n and we can skip to the next line; if it starts with a comment/literal marker that doesn't consist of repeated characters (such as /*) we can in most cases simply delete the first character of the marker to uncomment the chaff code, which will also typically break a program because the chaff isn't valid code. (Python also has ''' and """ as literal markers, but these have an odd length so we damage the marker rather than the useless contents inside.) So there's a reasonable chance that the first or second character we delete will end up breaking the program we're attacking.

Installation

Install appropriate packages for building C programs (on Ubuntu, this is build-essential), and download https://catseye.tc/distfiles/fbbi-1.0-2015.0729.zip, then:

unzip fbbi-1.0-2015.0729.zip 
cd fbbi-1.0-2015.0729/
(cd src; make)

Run programs using bin/ffbi programfilename (while in the ffbi-1.0-2015.0729/ directory created during the install).

Explanation

Radiation hardening

This is based on a technique discovered by Martin Ender for this answer, which is a little difficult to explain to people who don't know Befunge-98, but I'm going to try anyway.

It works using the x command; you can think of this command as taking a pair of coordinates interpreted as a vector, and creating something line-like (a "Lahey line") in 2D space by repeatedly moving in the direction of the vector (and the exact opposite direction) starting from itself. For example, if an x is given the coordinates (2,1), it creates the following Lahey line (which is also an actual line) in 2D space (marked with + signs), stretching out to infinity in both directions:

+..........
..+........
....x......
......+....
........+..
..........+

Unlike actual lines, though, which pass through every point on them, specifying a vector like (2, 0) gives you a Lahey line that only passes through some of the squares along it:

.+.+.+.x.+.+.+.+.

While the x command is in effect, the Befunge-98 interpreter basically acts as though all the commands that aren't on the Lahey line don't exist. This means that if we can set up an appropriate Lahey line that passes through every fifth character of the program (starting at the first x that remains in the program after radiation damage), and every character from then on (including the x) is repeated five times, any four characters can be deleted from the program and yet it will still have identical behaviour to the original version – in either case, the set of non-hidden characters from the x onwards are identical. (The idea is that before the location of the first deletion, the Lahey line passes through the first character of each set of five; between the first and second deletions, it passes through the second character of each set of five, etc., with the last character of the set being used after the fourth deletion.)

Befunge has a lot of commands that cancel the effect of x (including, most notably, x itself, plus pretty much all the flow control commands). That made this program a fairly awkward challenge, as I had to write some nontrivial program logic with most of the flow control commands missing. However, the fact that x cancels the old Lahey line before creating a new one is useful in setting up the appropriate Lahey line delta, of (5, 0). The idea is to start by putting a lot of 5s and 0s on the stack in the repetitive 050 050 050 … pattern (the stack starts with a zero on it, so the leading 0 can be omitted); each of these patterns (if non-irradiated) will be interpreted as either (5, 0) or (0, 5) depending on its distance from the top of the stack, and the interpretation of two adjacent patterns alternates between (5, 0) or (0, 5) unless radiation hits the upper copy of the pattern. Because there are many more than four copies of the pattern on the stack, there's thus guaranteed to be a (5, 0) pair on the stack somewhere if it's interpreted as pairs from the top. Also, there's nothing but 0s and 5s on the stack, so any pair must be (0, 0), (0, 5), (5, 0), or (5, 5).

Now, consider what happens when the x command gets run. If its argument is (5, 0), it creates the Lahey line that we want, and the program continues in its radiation-hardened way. If it's (0, 0), (0, 5) or (5, 5), then the Lahey line that's created misses our program altogether; (0, 5) and (5, 5) are vertical and diagonal lines that cross our program only at a single point, and the Lahey line (0, 0) is a special case that's inherently a single point. In these cases, therefore, everything in the program apart from the x we just ran gets hidden, so the interpreter has no choice but to run the x again (exiting is always explicit in Befunge), taking the next pair from the stack. Thus, with a one-liner program like this, the behaviour of x inherently looks at pairs of values at a time along the stack until it finds the (5, 0) that must exist, and thus is guaranteed to create a Lahey line that runs along the program in the correct way to radiation-harden it.

(There are many more 050 patterns in this program than are actually required for the radiation hardening to survive four deletions – the idea is to be able to survive for more than four rounds if the opponent is primarily attacking the start of the program, because most damage there is survivable; to eliminate the (5, 0) pattern entirely, a number of deletions equal to the number of 5s is required.)

How to do flow control

When writing the main body of the program, because almost all flow control commands cancel the Lahey line, ; … ;# and kr are pretty much all I had to work with.

; … ; is a jump, and adding #s in various places controls whether the jump is taken or skipped when executing the code in forward and/or reverse order (it also sometimes has the unfortunate side effect of skipping one character beyond the end of the jump, because # is a skip command, but this is easy to work around by repositioning characters; it just makes the program harder to read).

kr is a method of creating a conditional without breaking the Lahey line, but it's very quirky (the semantics of k are famously confusing). This combination pops the stack, then does nothing if a 0 was popped, reverses the execution direction if an odd or negative number was popped (for different reasons in the two cases), and its semantics are too complicated to easily describe if an even positive number was popped – this program therefore uses only the arguments 0 and 1, to keep the behaviour of kr easy to understand.

r on its own reverses the execution direction unconditionally, which was useful in one place (as #r, which reverses the execution direction only if it's currently going to the left).

As usual for Befunge-98, it's possible to replace r with any invalid command, so I picked NUL, plus two high-bit-set characters that aren't valid UTF-8, in the hope of confusing programs that don't have binary-safe input routines. (FBBI itself is binary-safe if run on a Unix-like system, like the one being used in this challenge.) The command in question is nonetheless written as r in this post, as that's the "official" name for it.

Doing the actual work

Once the x command gets hit, here's what the remaining program along hte Lahey line looks like, and how it works (there's also some junk 0s and 5s at the start but they never again get executed):

xn#;~:~-!!;@,.;#\kr'#-:c-*#r!;#-a~+;#1kr2+;
x                                            establish Lahey line
 n                                           clean stack (pop all the 5s)
  #;                                         jump label, no-op right now

From this point onwards, the element currently on the top of the stack (which at this point is a 0 because the stack has just been cleaned) counts the number of bytes that the program has read.

xn#;~:~-!!;@,.;#\kr'#-:c-*#r!;#-a~+;#1kr2+;
    ~                                        read byte
     :                                       copy it
      ~                                      read another byte
       -                                     subtract
        !!                                   cast to bool (like in C)
          ;   ;#                             jump … to jump label + skip
                 kr                          reverse direction if truthy

A truthy value at this point means that the two bytes we read were different, so it's time to select the first of the two bytes and exit the program. Top of the stack at this point is the first of the two bytes; the second stack element is its index (because it's still unchanged from the start of the main loop).

xn#;~:~-!!;@,.;#\kr'#-:c-*#r!;#-a~+;#1kr2+;
                \                            swap
               ;#                            jump label, no-op going left
              .                              output integer then space
             ,                               output raw byte
            @                                exit program

Very convenient that Befunge's "output integer then space" command is usable in this challenge (because it requires a space after the integer)! If the format didn't allow/require a space there, the integer-to-decimal conversion would take up much, much more room and probably reduce the amount of radiation hardening available as a consequence.

If the two bytes the program read are the same, the program continues by checking to see if they're // and/or ##, via arithmetic on character codes:

xn#;~:~-!!;@,.;#\kr'#-:c-*#r!;#-a~+;#1kr2+;
                   '#-                       subtract char code of `#`
                      :  *                   multiply by (itself
                       c-                      minus 12)
                          #r                 no-op if going right
                            !                logical not
                             ;     ;#        jump … to jump label + skip
                                      kr     reverse direction if truthy

The direction reverse here happens if, in C syntax, !(d*(d-12)), where d is the first byte of the pair minus the character code of #; # and / have character codes that differ by 12, so this is true if the byte is # or / (the multiplication * is being used like a logical AND). In this case, the strategy is to skip until finding a newline. The top of the stack at this point is the byte counter, because everything else has been popped, and that gets update while looking for the newline:

xn#;~:~-!!;@,.;#\kr'#-:c-*#r!;#-a~+;#1kr2+;
                                  +  1       add 1 to top of stack
                                   ;#        jump label, no-op going left
                                 ~           read byte
                               -a            subtract 10
                             ;#              jump label, no-op going left
                            !                logical not
                           r                 reverse direction
                            !                logical not again
                             ;     ;#        jump … to jump label + skip
                                      kr     reverse direction if truthy

In other words, the program adds 1 to the byte counter, then reads the next byte, and continues doing this until a newline was read. (This shares some of the code from the previous case – in that case, the ! is executed once, so that the loop is exited/not entered if the character isn't / or #, but in this loop, the same ! is executed twice, so that the loop is exited if the character is a newline. In one-line Befunge-98, opportunities like this to use the same character in both directions are rare, but fun when they happen.)

If the byte pair was something other than // or ##, or when the newline has been found, the program's main loop continues:

xn#;~:~-!!;@,.;#\kr'#-:c-*#r!;#-a~+;#1kr2+;
                                        2+   add 2 to top of stack
  #;                                      ;  jump … to jump label

This skips over the xn at the start, and also skips over any of the 0s and 5s that happen to fall on the Lahey line. (In Befunge-98, execution wraps, but it wraps along the Lahey line rather than toroidally around the program, so the same set of characters will be executed after the wrap; the program's length modulo 5 is irrelevant.)

The program "forgot" to add 2 to the byte counter after the pair was checked, so it gets done here; the reason for postponing it is that less stack manipulation is needed to set it here (because there's no need to remember the first character of the pair at this location in order to check whether it's / or #, so the byte counter is the only thing on the stack at this point), and postponing it still produces the correct results because addition is commutative, so it doesn't matter whether we add the 2 before or after adding the 1s. (The skip-to-newline loop doesn't actually inspect the byte counter, just increment it.)

This program doesn't actually work for all inputs – I didn't add any code to handle EOF, rather the hope is that it won't be given input for which the algorithm reaches EOF before reaching a termination condition. I think this is probably a reasonable tradeoff – code to handle EOF given the stringent restrictions on control flow would make the program much longer and thus reduce the amount of radiation it could withstand, as I'd have to use a higher-frequency Lahey line to fit it all into 256 bytes.

\$\endgroup\$
5
  • \$\begingroup\$ Note that #.......\n is also a commment \$\endgroup\$
    – Seggan
    Commented Aug 19, 2022 at 14:34
  • \$\begingroup\$ @Seggan If the start-of-comment marker isn't doubled, then it's probably possible to break the target program by removing the leading #,dumping the contents of the comment into the program, so there's no need to skip the comment in that case. \$\endgroup\$
    – ais523
    Commented Aug 19, 2022 at 14:37
  • \$\begingroup\$ Ok this hangs after it has taken some damage, will need to modify the controller so it can kill a running process \$\endgroup\$ Commented Aug 19, 2022 at 15:59
  • 1
    \$\begingroup\$ That doesn't surprise me – an infinite loop is quite a likely consequence of damage in Befunge-98, as the vast majority of error conditions reverse the flow of execution rather than exiting the program. \$\endgroup\$
    – ais523
    Commented Aug 19, 2022 at 16:02
  • 1
    \$\begingroup\$ Ok I got it working now. Interestingly it seems to have formed a symbiotic relationship with the example python bot, moving it from last to second place. \$\endgroup\$ Commented Aug 19, 2022 at 16:12
6
\$\begingroup\$

MVP - Python 3

import sys
print("0 "+sys.stdin.read()[:1])

Try it online!

MVP - (noun) Minimum Viable Product. This bot meets all the requirements set out in the challenge with 43 bytes. It is unsurprisingly not particularly radiation-resistant.

Installation

apt-get install python3

How to run

Depending on how you have Python installed:

python code.py 

or

python3 code.py
\$\endgroup\$
3
  • 6
    \$\begingroup\$ Welcome to Code Golf! This would probably go against the bot's goal, but maybe adding a few hundred spaces in a comment or something could make it slightly more competitive without ruining the simplicity. \$\endgroup\$
    – rydwolf
    Commented Aug 21, 2022 at 1:51
  • \$\begingroup\$ Thanks you for providing complete build instructions \$\endgroup\$ Commented Aug 21, 2022 at 5:41
  • \$\begingroup\$ I think PEP 8 recommends a blank line after imports, and spaces around the '+' operator. \$\endgroup\$
    – Oskar Skog
    Commented Aug 25, 2022 at 21:56
4
\$\begingroup\$

Vyxal, The Terrible Bot

□⁋□⁋###□⁋□⁋
;;}□⁋□⁋□⁋ ėλntt¶ntt=¬;;;Ḋƛ'kwntc¬; λ\#\#\/\#\/JJJJnttc¬;Ẏ;;;Þf℅h :::: □⁋i \   pJ
;;}□⁋□⁋□⁋ ėλntt¶ntt=¬;;;Ḋƛ'kwntc¬; λ\#\#\/\#\/JJJJnttc¬;Ẏ;;;Þf℅h :::: □⁋i \   pJ
;;}□⁋□⁋□⁋ ėλntt¶ntt=¬;;;Ḋƛ'kwntc¬; λ\#\#\/\#\/JJJJnttc¬;Ẏ;;;Þf℅h :::: □⁋i \   pJ

Try it Online!

Base64-encoded:

4pah4oGL4pah4oGLIyMj4pah4oGL4pah4oGLCjs7feKWoeKBi+KWoeKBi+KWoeKBiyDEl867bnR0wrZudHQ9wqw7OzvhuIrGmydrd250Y8KsOyDOu1wjXCNcL1wjXC9KSkpKbnR0Y8KsO+G6jjs7O8OeZuKEhWggOjo6OiDilqHigYtpIFwgICBwSgo7O33ilqHigYvilqHigYvilqHigYsgxJfOu250dMK2bnR0PcKsOzs74biKxpsna3dudGPCrDsgzrtcI1wjXC9cI1wvSkpKSm50dGPCrDvhuo47OzvDnmbihIVoIDo6Ojog4pah4oGLaSBcICAgcEoKOzt94pah4oGL4pah4oGL4pah4oGLIMSXzrtudHTCtm50dD3CrDs7O+G4isabJ2t3bnRjwqw7IM67XCNcI1wvXCNcL0pKSkpudHRjwqw74bqOOzs7w55m4oSFaCA6Ojo6IOKWoeKBi2kgXCAgIHBK

Installation

pip install vyxal

Running

vyxal code.vy
\$\endgroup\$
12
  • \$\begingroup\$ Thank you for being the first to have complete build instructions. Unfortunately, it seems the bot produces invalid output. You need to output index of the character you want to remove, followed by a space, followed by the literal character value. \$\endgroup\$ Commented Aug 19, 2022 at 19:39
  • \$\begingroup\$ I would appreciate if you included a base64 encoded version just so I'm sure there are no encoding issues during copy pasting \$\endgroup\$ Commented Aug 19, 2022 at 19:50
  • \$\begingroup\$ The base64 version seems to be for the old version, it's too long \$\endgroup\$ Commented Aug 19, 2022 at 20:22
  • \$\begingroup\$ All my code works on bytes and not on strings \$\endgroup\$ Commented Aug 19, 2022 at 20:25
  • 1
    \$\begingroup\$ Unfortunately, the entire controller is based on sending things to STDIN so I can't make a exception for one program. However, I'd happily use a vyxal version from your fork until the PR is approved if you tell me how to install it \$\endgroup\$ Commented Aug 20, 2022 at 5:18
4
\$\begingroup\$

Natural Radiation - J

              (       ]                   (      (                    [:        ":           [          ),          ' ',               {      )    ~            [:                  ?         #         )          (1!:1@3              '')                   

Try it online! (although TIO won't accept EOF signals/characters for input as far as I'm aware, so it may not work as intended)

Installation

(source)

These instructions assume you are on a x86_64 processor. If you are on armv7/armv8/aarch64, use the Raspberry Pi downloads in the link above.

Prior to installation, you may need to install necessary libraries:

apt-get install libqtgui4 libqt4-svg libqt4-svg libqt4-opengl libqt4-network libqtwebkit4

Then, save the installation archive (recommended location is the home folder, but can be installed anywhere else with sufficient permissions) with a command similar to

wget http://www.jsoftware.com/download/j903/install/j903_linux64.tar.gz

Now extract the archive. There should be a folder called j903. Inside the folder, there should be a file called jconsole.sh.

How to run

Option 1

./jconsole.sh

Then, paste the source code above in to the console (followed by a newline), or pass it in by script.

Option 2

Run the code below. The additional echo is necessary due to the limitations of program output when running directly from script. The echo is generally not counted as part of the source code. In addition, there is an additional character inside the body due to the need to escape the " character.

./jconsole.sh -js "echo               (       ]                   (      (                    [:        \":           [          ),          ' ',               {      )    ~            [:                  ?         #         )          (1!:1@3              '')                   "

Option 3

Save source code into file and run:

./jconsole.sh code.ijs

However, there are some caveats. Firstly, echo may need to be added to the beginning of the code to get the output to work correctly (like in Option 2) due to how J is primarily an interpreted language. Also, the appearance of an EOF in stdin may cause extra undesired output to be written to stderr. Finally, I have not tested this on an Ubuntu Linux device as I do not have access to one and am currently unable to access Docker to set one up.

Explanation

Just like natural random radiation events, this bot will choose a random character to remove from its target. The bot itself has some basic radiation resistance, but is unable to handle targeted radiation attacks.

\$\endgroup\$
3
  • \$\begingroup\$ I ran the code like this: echo "echo" > code.ijs && cat code >> code.ijs && j/j903/jconsole.sh ../../code.ijs but I'm getting the error Syntax Error Numerical Value not Entered. \$\endgroup\$ Commented Aug 22, 2022 at 7:17
  • \$\begingroup\$ @mousetail Can you try running it with Option 2 above? It's not the cleanest solution, but it worked on my computer. \$\endgroup\$
    – huanglx
    Commented Aug 22, 2022 at 11:32
  • \$\begingroup\$ Ok I spent a lot of time trying to get this to work now but to no avail. Feel free to submit a pull request to the controller with a working setup but otherwise I'm afraid I need to give up. \$\endgroup\$ Commented Aug 23, 2022 at 6:33
3
\$\begingroup\$

Brainfuck, 168 bytes, "the 2nd'er"

,#(((((((
>+++++++!
+++++++;/
+++++++//
+++++++/*
+++++++*/
+(+(+(+(+
+#{#+#}#+
!!+++++!!

|v.v|
----|
----v
----|
----v
.<.</
LCMoKCgoKCgoCj4rKysrKysrIQorKysrKysrOy8KKysrKysrKy8vCisrKysrKysvKgorKysrKysrKi8KKygrKCsoKygrCisjeyMrI30jKwohISsrKysrISEKCnx2LnZ8Ci0tLS18Ci0tLS12Ci0tLS18Ci0tLS12Ci48Ljwv
\$\endgroup\$
4
3
\$\begingroup\$

Sorry, Self-modifying Brainfuck

<<<<<<<<<<<<<<<<<<<<<<<<<<<<.<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<.<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<,.                                                        00000000000000000000000000000000000000000000000000000000

Try it online!

Install (assumes gcc and wget):

wget https://soulsphere.org/hacks/smbf/smbf.c
gcc smbf.c -o smbf

Run (assumes the source stored as src.smbf in the same folder as smbf executable):

./smbf src.smbf

Does the most obvious thing, maximizing defense against random deletions and nothing else.

\$\endgroup\$
3
\$\begingroup\$

Piet + ascii-piet, 256 bytes (4×86=344 codels), picture perfect v3

jsamtfjsamtfjsamtfjsaqdusiiiiaaaabbbbddddffffjjjjvvvvtttteeeeeeeeeeeeeeeeeeeeeeeeeeeeEc?b?bed??j???bkaubuebmvmk                eeeeeeeeeeeeeeeeeeeeeeeeeee             e  Edjfvetejkbbraqmusqemqudsiadbdiab              eeeeeeeeeeeeeeeeeee               ee eE

Base64 encoded

anNhbXRmanNhbXRmanNhbXRmanNhcWR1c2lpaWlhYWFhYmJiYmRkZGRmZmZmampqanZ2dnZ0dHR0ZWVlZWVlZWVlZWVlZWVlZWVlZWVlZWVlZWVlZUVjP2I/YmVkPz9qPz8/YmthdWJ1ZWJtdm1rICAgICAgICAgICAgICAgIGVlZWVlZWVlZWVlZWVlZWVlZWVlZWVlZWVlZSAgICAgICAgICAgICBlICBFZGpmdmV0ZWprYmJyYXFtdXNxZW1xdWRzaWFkYmRpYWIgICAgICAgICAgICAgIGVlZWVlZWVlZWVlZWVlZWVlZWUgICAgICAgICAgICAgICBlZSBlRQ==

Try Piet online!

This bot probably won't do too well, considering that all it does is remove the first character each time, but hopefully it survives moderately well to place top 10 :P

V2: Hopefully fixed the vulnerability that I noticed when I took a look in the leaderboards.

V3: Fixes a vulnerability where removing the first 5 characters is fatal. Also added some protection against Thief, which hopefully works.

(credits to @Bubbler for the installation and run instructions)

Installation

pip install Pillow
git clone https://github.com/cincodenada/bertnase_npiet.git piet
cd piet
./configure
make npiet
cd ..
git clone https://github.com/dloscutoff/ascii-piet.git ascii-piet

Run

Save the source code in the text file src.txt:

python3 ascii-piet/ascii2piet.py src.txt src.png && piet/npiet -q src.png
\$\endgroup\$
2
\$\begingroup\$

NÜL tränslätör - Bash

Just run it with bash, it should work (TM).

##((()
##(()

i=$(tr  '\0'  ' ')

s=$(printf  %s  "$i"|wc  -c)

[  $s  -gt  -1  ]||s=$(printf  %s  "$i"|wc  -c)

r=$((RANDOM%s))

[  $r  -gt  -1  ]||r=$((RANDOM%s))

printf  "%d "  $r

p=$((r+1))

p=$((r+1))

printf  %s  "$i"|head  -c  $p|tail  -c1
##((()

It can survive some parts being broken, but unfortunately not syntax errors. Eg. if $s isn't a valid integer, [ will fail and s will be reassigned.

IyMoKCgpCiMjKCgpCgppPSQodHIgICdcMCcgICcgJykKCnM9JChwcmludGYgICVzICAiJGkifHdj
ICAtYykKClsgICRzICAtZ3QgIC0xICBdfHxzPSQocHJpbnRmICAlcyAgIiRpInx3YyAgLWMpCgpy
PSQoKFJBTkRPTSVzKSkKClsgICRyICAtZ3QgIC0xICBdfHxyPSQoKFJBTkRPTSVzKSkKCnByaW50
ZiAgIiVkICIgICRyCgpwPSQoKHIrMSkpCgpwPSQoKHIrMSkpCgpwcmludGYgICVzICAiJGkifGhl
YWQgIC1jICAkcHx0YWlsICAtYzEKIyMoKCgpCg==

Non-competing version

This one can handle NUL bytes, I think.

##()()
##()()

f=ooga

f=ooga

cp  /dev/stdin  $f

s=$(wc  -c < $f)

[  $s  -gt  -1  ]||s=$(wc  -c < $f)

r=$((RANDOM%s))

[  $r  -gt  -1  ]||r=$((RANDOM%s))

printf  "%d "  $r

p=$((r+1))

p=$((r+1))

(head  -c  $p  $f||head  -c  $p  $f)|tail  -c1
##()()

But it saves input in a temp file violating rule 2.

\$\endgroup\$
1
  • \$\begingroup\$ Nice, one of the first bots that worked for me the first try with no configuration \$\endgroup\$ Commented Aug 22, 2022 at 11:14
2
\$\begingroup\$

V (vim), Golf Core

00000000: 1b1b 1b1b 1b1b 1b1b 1b1b 1b1b 1b1b 1b1b  ................
00000010: 1b1b 1b1b 1b1b 1b1b 1b1b 1b1b 1b1b 1b1b  ................
00000020: 1b1b 1b1b 1b1b 1b1b 1b1b 1b1b 1b1b 1b1b  ................
00000030: 1b1b 1b1b 1b1b 1b1b 1b1b 1b1b 1b1b 1b1b  ................
00000040: 3132 3334 3536 3738 394a 4a4a 4a4a 4a4a  123456789JJJJJJJ
00000050: 4a4a 4a4a 4a4a 4a4a 4a4a 4a4a 4a20 4449  JJJJJJJJJJJJJ DI
00000060: 3020 1b1b 1b1b 1b1b 1b1b 1b1b 1b1b 1b1b  0 ..............
00000070: 1b1b 1b1b 1b1b 1b1b 1b1b 1b1b 1b1b 1b1b  ................
00000080: 2f2f 2f2f 2f2f 2f2f 2f2f 2f2f 6162 6365  ////////////abce
00000090: 6620 6768 696b 6d20 6e6f 7071 7220 7374  f ghikm nopqr st
000000a0: 7576 7720 7879 7a41 4220 4345 4648 4a20  uvw xyzAB CEFHJ
000000b0: 4b4c 4d4e 4f20 5051 5253 5420 5557 5859  KLMNO PQRST UWXY
000000c0: 5a20 2829 5b5d 7b7d 2020 2020 2028 5b5d  Z ()[]{}     ([]
000000d0: 7b7d 2920 285b 7b7d 5d29 20e5 a4a9 e59c  {}) ([{}]) .....
000000e0: b0e7 8e84 e9bb 84e5 ae87 e5ae 99e6 b4aa  ................
000000f0: e88d 920d 0d0d 0d0d 0d0d 0d0d 1b1b 1b1b  ................

Try it online!

A stupid program which always remove the first character from input. Try to confuse other programs by the unprintable bytes. The only thing that make sense here is 89J DI0 <esc>.


I'm not sure if this is valid since V doesn't read from stdin but from a file. (I failed to make it read from stdin.)

Starting from Python 3.6 docker container (you may need to mount source code folder into the container so it could be used later)

docker run -it --name=thev python:3.6 /bin/bash

Download source code and install all dependency

cd ~
pip install docopt neovim trollius
apt update
apt install neovim -y
apt install xterm -y
wget https://github.com/DJMcMayhem/V/archive/refs/heads/master.zip
unzip master.zip
cd V-master

Place the source code into the container some how

xxd -r > ~/program.v
  (paste the xxd source code in this post)

Open another terminal, and run the program

docker exec -i thev /bin/bash -c 'cat - > ~/input && ~/V-master/v ~/program.v -f ~/input'
\$\endgroup\$
3
  • \$\begingroup\$ Installation instructions? \$\endgroup\$ Commented Aug 19, 2022 at 6:11
  • \$\begingroup\$ Also I need the command to run this on the command line \$\endgroup\$ Commented Aug 19, 2022 at 6:37
  • 1
    \$\begingroup\$ @mousetail I had included an instruction. I'm not sure if this is valid. And Python 3.6 is required. It failed to work on Python 3.5 or Python 3.7. \$\endgroup\$
    – tsh
    Commented Aug 22, 2022 at 11:50
2
\$\begingroup\$

Tapestry, <><

No good strategy, focuses on being very hardened. Each character is at least 4x redundant.

iiiiiiiivvvvvvv



lllllllllllllll



111111111111111



---------------



nnnnnnnnnnnnnnn



888888888888888



222222222222222



222222222222222



***************



***************



ooooooooooooooo



ooooooooooooooo



;;;;;;;;;;;;;;;

Explanation:

  1. iiiiiiii push the first 8 characters of our opponent to the stack.
  2. v go down
  3. l get the length of the stack. In case one of the i was deleted
  4. 1 push one to the stack
  5. - subtract 1 to get a 0 based index
  6. n print this as a number, this is the index
  7. 822** push "32" to the stack, the ASCII code for space
  8. o print a space
  9. o print the original character
  10. ; end the program

Setup

if [[ ! -f fish.py ]]
then
            wget https://gist.github.com/anonymous/6392418/raw/3b16018cb47f2f9ad1fa085c155cc5c0dc448b2d/fish.py
fi

Run

python3 fish.py code
\$\endgroup\$
1
2
\$\begingroup\$

The Newline Killer, 172 Bytes, In response to the new edit in this.

//////
////##
////((
////>>

k,j;main(i){for(j=getchar();i<999;i++){k=getchar();if(k=='\n'||k=='='){printf("%d %c",i,k);exit(0);}}printf("0 %c",j);}

//////
////##
////((
////>>

I managed to do this without learning C I am so smart (this is written in C).

Targets the first new line or =. Hopefully there won't be too much irrelevant =s. Else targets the first character. Most of the code has comments which can cause them to break by commenting their own code.

\$\endgroup\$
2
  • \$\begingroup\$ This seems to be 176 bytes, not 172. \$\endgroup\$
    – Aiden Chow
    Commented Aug 25, 2022 at 8:17
  • \$\begingroup\$ ok, will change that \$\endgroup\$ Commented Aug 25, 2022 at 8:42
2
\$\begingroup\$

Python3, Key Characters Attack, 255 bytes

# # # # # # # repetitionrepetitionrepetition=lxn_1";=lxn_1";$(>+%cstststststststststststst\n\n\nfrom sys import stdin as s;\ni=s.read();\nfor c in'\\n )}.01+\\n )}.01+'[len(i)%8:][:8]:\n if c in i:print(f'{i.index(c)} {c}');exit();\n\nprint(f'{len(i)-1} {i[-1]}');

Which is:

 # # # # # # # repetitionrepetitionrepetition=lxn_1";=lxn_1";$(>+%cstststststststststststst


from sys import stdin as s;
i=s.read();
for c in'\n )}.01+\n )}.01+'[len(i)%8:][:8]:
 if c in i:print(f'{i.index(c)} {c}');exit();

print(f'{len(i)-1} {i[-1]}');

Base 64:

ICMgIyAjICMgIyAjICMgcmVwZXRpdGlvbnJlcGV0aXRpb25yZXBldGl0aW9uPWx4bl8xIjs9bHhuXzEiOyQoPislY3N0c3RzdHN0c3RzdHN0c3RzdHN0c3RzdAoKCmZyb20gc3lzIGltcG9ydCBzdGRpbiBhcyBzOwppPXMucmVhZCgpOwpmb3IgYyBpbidcbiApfS4wMStcbiApfS4wMSsnW2xlbihpKSU4Ol1bOjhdOgogaWYgYyBpbiBpOnByaW50KGYne2kuaW5kZXgoYyl9IHtjfScpO2V4aXQoKTsKCnByaW50KGYne2xlbihpKS0xfSB7aVstMV19Jyk7

The repetition was due to this answer.

The next was to target this answer, and to prevent it from targeting the actual stdin.

The comment up front avoids being target by programs that target the first or second letters, like this answer.

The comment is # # instead of # or ### to be robust towards first letter targetters and this answer, which targets ##.

As in this answer, we target different things every time. We go through \n, , ), }, ., 0, 1, and +, but \n takes priority in the first run, in the second, et cetera. We decide that newlines, spaces, closing parenthesis, decimals, numbers, and the plus sign are rarely fake and usually used, while being very vulnerable.

Quick fix: added more comments due to the feedback.

\$\endgroup\$
2
  • 2
    \$\begingroup\$ Isn't repetition actively bad for the purpose of defending against wheel? \$\endgroup\$ Commented Aug 25, 2022 at 11:50
  • \$\begingroup\$ Well, it isn't making it worst, right? \$\endgroup\$ Commented Aug 25, 2022 at 13:12
2
\$\begingroup\$

Thief - C (gcc 8.3)

Sneaks in through the backdoor to steal your rarest most valued characters.

(edited to add two more slashes in the beginning, edited again to add four more nulls at the end)

00000000: 2f2f 2f2f 2f2f 2a2a ff20 2029 7d2e 3031  //////**.  )}.01
00000010: 2b25 2563 2828 ff28 5d2c 702c 6e74 6628  +%%c((.(],p,ntf(
00000020: 2225 6420 2563 222c 505b 635d 2c63 293b  "%d %c",P[c],c);
00000030: 7d5b 323d 702b 2b3b 7768 696c 6528 6d61  }[2=p++;while(ma
00000040: 696e 2863 297b 750a 0a0a 2f2f 2f2a 2a2f  in(c){u...///**/
00000050: 0a0a 0a00 6766 2c70 713b 3b00 465b 3235  ....gf,pq;;.F[25
00000060: 365d 2c50 5b32 3536 5d2c 702c 663b 6d61  6],P[256],p,f;ma
00000070: 696e 2863 297b 666f 7228 3b28 633d 0020  in(c){for(;(c=. 
00000080: 000a 0020 000a 0020 000a 0020 000a 0000  ... ... ... ....
00000090: 0067 6574 6368 6172 2829 293e 2d31 3b46  .getchar())>-1;F
000000a0: 5b63 5d2b 2b29 505b 635d 3d70 2b2b 3b77  [c]++)P[c]=p++;w
000000b0: 6869 6c65 282b 2b66 3c32 3537 2966 6f72  hile(++f<257)for
000000c0: 2863 3d32 3536 3b2d 2d63 3e2d 313b 2969  (c=256;--c>-1;)i
000000d0: 6628 465b 635d 3d3d 6629 7265 7475 726e  f(F[c]==f)return
000000e0: 2020 7072 696e 7466 2822 2564 2025 6322    printf("%d %c"
000000f0: 2c50 5b63 5d2c 6329 3b7d 0000 0000 0000  ,P[c],c);}......

The core of this is this 138 byte long string

F[256],P[256],p,f;main(c){for(;(c=getchar())>-1;F[c]++)P[c]=p++;while(++f<257)for(c=256;--c>-1;)if(F[c]==f)return printf("%d %c",P[c],c);}

How it works

It searches for the least frequent characters, and of those the last occurrence of the character of the highest value is selected.

// Variables are `int` by default.  Global variables are initialized to zero.

F[256],     // Array of frequency for each character value
P[256],     // Array of last position for each character value
p,          // position counter variable
f;          // frequency iterator variable

main(c)     // main function, integer variable for character (argc)
{
    // Read input and count frequency and position
    for(;
        (c = getchar()) > -1;   // Read character in while condition
        F[c]++                  // Update frequency at end of loop
    )
        P[c] = p++;             // Update last position for character

    // Find the least frequent character [1..256]
    while (++f < 257)
        // Find the highest valued character [255..0]
        for (c = 256; --c > -1;)
            // First match is the best match
            if (F[c] == f)
                return printf("%d %c",P[c],c);
}

Hardening

  • Many unsuccessful attempts to deceive The Burrowing Wheel
  • Not much can be done against random targeting bots since the core is so huge.
  • Lots of padding in the front to handle a variety of bots removing the 1st (many), 1st to 8th (Tapestry), 10th (Bastarf) and 21st (Redun) character. Inside the damage resistant comment there are traps for Key Characters Attack, Bastarf, Parenthesis Killer and Ghost. Also included are some invalid UTF-8 bytes and null bytes to hopefully break something before it reaches anything important.
  • The middle of the program is attacked by the example Python bot
  • Source code is 7.4% null characters.

base64 (v3)

Ly8vLy8vKir/ICApfS4wMSslJWMoKP8oXSxwLG50ZigiJWQgJWMiLFBbY10sYyk7fVsyPXArKzt3
aGlsZShtYWluKGMpe3UKCgovLy8qKi8KCgoAZ2YscHE7OwBGWzI1Nl0sUFsyNTZdLHAsZjttYWlu
KGMpe2Zvcig7KGM9ACAACgAgAAoAIAAKACAACgAAAGdldGNoYXIoKSk+LTE7RltjXSsrKVBbY109
cCsrO3doaWxlKCsrZjwyNTcpZm9yKGM9MjU2Oy0tYz4tMTspaWYoRltjXT09ZilyZXR1cm4gIHBy
aW50ZigiJWQgJWMiLFBbY10sYyk7fQAAAAAAAA==
\$\endgroup\$
3
  • 1
    \$\begingroup\$ Very good job, you are currently ranked #1, very impressive for a practlang \$\endgroup\$ Commented Aug 26, 2022 at 9:34
  • \$\begingroup\$ How is it even possible?? \$\endgroup\$
    – Oskar Skog
    Commented Aug 26, 2022 at 10:37
  • \$\begingroup\$ There are 4 bots with extremely close scores right now \$\endgroup\$ Commented Aug 26, 2022 at 10:40
2
\$\begingroup\$

Protect and Shoot Randomly - 05AB1E, 256 bytes

III.....āÀÀÀÀÀ€RðýIII.....āÀÀÀÀÀ€RðýIII.....āÀÀÀÀÀ€RðýIII.....āÀÀÀÀÀ€RðýIII.....āÀÀÀÀÀ€RðýIII.....āÀÀÀÀÀ€RðýIII.....āÀÀÀÀÀ€RðýIII.....āÀÀÀÀÀ€RðýIII.....āÀÀÀÀÀ€RðýIII.....āÀÀÀÀÀ€RðýIII.....āÀÀÀÀÀ€Rðý))))))))))˜˜˜˜˜˜˜˜˜˜.r.r.r.r.r.r.r.r.r.r        MMMMMMMMMM

Input is a multi-line string.

Try it online. (Using the current highest voted answer as example input program.)

My initial idea was to always target the middle character of a given program, but this proved to be too tricky in combination with the radiant hardening (primarily because 05AB1E's middle builtin Ås will give two results if the argument has an even length). So instead, a much more boring but more stable approach: it (tries to) output a random target.

Encoding:

05AB1E uses a custom codepage, so here are the raw bytes instead:

\x49\x49\x49\x2E\x2E\x2E\x2E\x2E\xA0\xC0\xC0\xC0\xC0\xC0\x80\x52\xF0\xFD\x49\x49\x49\x2E\x2E\x2E\x2E\x2E\xA0\xC0\xC0\xC0\xC0\xC0\x80\x52\xF0\xFD\x49\x49\x49\x2E\x2E\x2E\x2E\x2E\xA0\xC0\xC0\xC0\xC0\xC0\x80\x52\xF0\xFD\x49\x49\x49\x2E\x2E\x2E\x2E\x2E\xA0\xC0\xC0\xC0\xC0\xC0\x80\x52\xF0\xFD\x49\x49\x49\x2E\x2E\x2E\x2E\x2E\xA0\xC0\xC0\xC0\xC0\xC0\x80\x52\xF0\xFD\x49\x49\x49\x2E\x2E\x2E\x2E\x2E\xA0\xC0\xC0\xC0\xC0\xC0\x80\x52\xF0\xFD\x49\x49\x49\x2E\x2E\x2E\x2E\x2E\xA0\xC0\xC0\xC0\xC0\xC0\x80\x52\xF0\xFD\x49\x49\x49\x2E\x2E\x2E\x2E\x2E\xA0\xC0\xC0\xC0\xC0\xC0\x80\x52\xF0\xFD\x49\x49\x49\x2E\x2E\x2E\x2E\x2E\xA0\xC0\xC0\xC0\xC0\xC0\x80\x52\xF0\xFD\x49\x49\x49\x2E\x2E\x2E\x2E\x2E\xA0\xC0\xC0\xC0\xC0\xC0\x80\x52\xF0\xFD\x49\x49\x49\x2E\x2E\x2E\x2E\x2E\xA0\xC0\xC0\xC0\xC0\xC0\x80\x52\xF0\xFD\x29\x29\x29\x29\x29\x29\x29\x29\x29\x29\x98\x98\x98\x98\x98\x98\x98\x98\x98\x98\x2E\x72\x2E\x72\x2E\x72\x2E\x72\x2E\x72\x2E\x72\x2E\x72\x2E\x72\x2E\x72\x2E\x72\x20\x20\x20\x20\x20\x20\x20\x20\x4D\x4D\x4D\x4D\x4D\x4D\x4D\x4D\x4D\x4D

Installation, Compilation, Execution:

Quoted from the 05AB1E docs:

05AB1E is written in Elixir using the Mix build tool, which comes with Elixir.

Installation

  1. Clone this repository (e.g. with git clone https://github.com/Adriandmen/05AB1E.git).
  2. Install Elixir 1.9.0 or higher using one of the installation options here.
  3. Install the package manager Hex with mix local.hex.

Compilation

Retrieve/update all necessary dependencies using mix deps.get (if necessary). In the terminal, compile the project using MIX_ENV=prod mix escript.build. On Windows in the command prompt, compile with set "MIX_ENV=prod" && mix escript.build.

Execution

After running the build command, a compiled binary file osabie will be generated. For example, running the file test.abe is done by running:

escript osabie test.abe

Normally, an 05AB1E file ends with .abe, but any other file extension can also be used.

Explanation:

Primarily focuses on , while trying to output a random index + character pair.
Let me start with an explanation of the core of the program:

I.ā€Rðý.rM
I           # Push the input multiline-string
 .ā         # Enumerate it, pairing each character with its 0-based index
   €R       # Reverse each inner pair from [char,index] to [index,char]
            # (05AB1E does have a single-byte builtin for this `í`,
            #  but this caused errors if certain characters were removed)
      ý     # Join each inner pair
     ð      # with a space (" ") delimiter to a string
       .r   # Shuffle the list of strings
         M  # Push a copy of the maximum (inner-most) value to the stack,
            # which basically takes the first character in the list in this case
            # (which is output implicitly as result)

Things to improve the radiation hardening:

  • Two I already mentioned: using M to get the first item in the list. I could have used н, but I can use multiple MMM in a row without changing the functionality, whereas multiple ннн would have resulted in the first digit of the index instead of the correct result-string.
  • And using €R instead of í, since can cause errors if ā is removed, and €ðý or Rðý are both fine, and doesn't give too much trash.
  • )))˜˜˜ are used to wrap the entire stack into a list, and then flatten it. The multiple ))) do of course result in an additional surrounding list every time, but the multiple ˜˜˜ are no-ops, as long as at least one of them is executed. If the ) is removed, it uses the last resulting list (if applicable), so the example C program that focuses on removing parenthesis won't do too much harm here.
  • The multiple .r.r.r to shuffle doesn't matter much either. And if either character is removed: .. is a no-op, and r is a reverse of the entire stack, which should only contain a single item (the list) at this point anyway.
  • Removing some other characters does result in trash in the list we shuffle and output a value from, but the chances aren't too high because we have repeated the program 11 times. E.g. Removing ý will put the indices and characters in the list as separated items, plus the additional pushed space (try it online). Removing ð or R will simply discard the list (try it online or try it online). Removing will put the index-character pairs in the wrong order (try it online). Removing ā will convert the input to a list of characters due to the , after which it's joined by spaces (try it online) - in addition, the .....ÀÀÀÀÀ will be some no-op .., one stack-rotate , and some list rotates À, all not changing the result too much. If āÀÀÀÀÀ would all be removed, the lists of the previous results would also be joined by spaces, which won't leave any correct results in the trailing list, hence why the À were added in the first place. Removing all ... in front of the ā will result in a loose list of (1-based) indices, which are then joined together by spaces (try it online). Removing a I doesn't matter, since I've added two more just in case (it's actually beneficial if some are removed, since the additional I input-strings are trash in the original base program) - removing all three will do an additional enumerate, giving an incorrect list with two indices per string (try it online).
  • And the spaces between the .r and M are no-ops. So removing or keeping those doesn't change anything to the program. They're simply used to fill in the remaining bytes to make the program length 256 (and to somewhat counter the Whitespace program). I've placed them in between those two, since .M if the last r is removed is the most-frequent builtin, which would always give the input-string, but will also take about 25-30 seconds to execute, so we'd like to prevent that with the execution time rule of <1 second.

Removing multiple characters in the program simultaneously may result in some undefined behavior. I haven't tested this too much, except for removing āÀÀÀÀÀ, which would probably be one of the worst case scenarios if it's done in the final subprogram of the total 11.

I have no idea how well it will do, since it's still pretty random. But overall it should do reasonably well (I think/hope).

\$\endgroup\$
9
  • \$\begingroup\$ I got a error running this: chat.stackexchange.com/transcript/message/61870349#61870349 \$\endgroup\$ Commented Aug 26, 2022 at 13:49
  • \$\begingroup\$ @mousetail I've added a second Bash TIO example which uses \n in the input-strings (instead of using a multiline """string"""), which uses EOFs for the input. Maybe implementing it like that in your controller script works slightly better? \$\endgroup\$ Commented Aug 26, 2022 at 14:34
  • \$\begingroup\$ Hmm.. although that second Bash example seems to output n instead of \n for the newlines.. :/ 🤔 \$\endgroup\$ Commented Aug 26, 2022 at 14:41
  • 1
    \$\begingroup\$ @OskarSkog It doesn't crash, it's just very slow and will always output the input-string after about 30 second: try it online. .M is the most-frequent builtin. But I guess with the rule of executing in less than a second, it does 'crash'. \$\endgroup\$ Commented Aug 27, 2022 at 11:12
  • 1
    \$\begingroup\$ @mousetail I've slightly changed my program by moving the leading spaces in between the .r and M to account for what OskarSkog mentioned above. EDIT: I also saw it didn't survive any first round, which really surprises me.. Are you sure it's run correctly? :S \$\endgroup\$ Commented Aug 27, 2022 at 11:16
2
\$\begingroup\$

Knight, 256 bytes, rookie v4

( (:( (:(:( (:( ( ( ( :(:(:( ( (: ( ( ( (:( (
;
=
s
''
;
=
i
~
1
;
WWWW
>
9999
=
i
+
1
i
=
s
+
s
IIII?=a+''PPPP'null'''+aAAAAJ10OOOOU+++''=a%RRRR-LLLLsTTTT' 'GGGGs aTTTT;;;;;;lnulnuunlsaaiaisas++=%%%%>>>>~~~~''''''''PARLTGIWARTLGPabcdefghijklmnopq101110100

Base64 encoded:

KCAoOiggKDooOiggKDooICggKCAoIDooOig6KCAoICg6ICggKCAoICg6KCAoCjsKPQpzCicnCjsKPQppCn4KMQo7CldXV1cKPgo5OTk5Cj0KaQorCjEKaQo9CnMKKwpzCklJSUk/PWErJydQUFBQJ251bGwnJycrYUFBQUFKMTBPT09PVSsrKycnPWElUlJSUi1MTExMc1RUVFQnICdHR0dHcyBhVFRUVDs7Ozs7O2xudWxudXVubHNhYWlhaXNhcysrPSUlJSU+Pj4+fn5+ficnJycnJycnUEFSTFRHSVdBUlRMR1BhYmNkZWZnaGlqa2xtbm9wcTEwMTExMDEwMA==

V2: I added more buffer at the beginning of my code. Hopefully that allows it to stay alive longer.

V3: I change the comments to parentheses so that I don't get destroyed by Number Basher's newline killer. It should also work well in fending against parentheses killer as well.

V4: Added some stuff at the end to counter any strategies which delete characters from the end.

Installation

Idk yet, here's a link to the code in an online interpreter instead with some test input: Try It Online!

\$\endgroup\$
1
  • \$\begingroup\$ Installation: Download this gist, and then to run, run Knight.run("code"); after executing that JS code. \$\endgroup\$
    – naffetS
    Commented Aug 24, 2022 at 16:51
1
\$\begingroup\$

redun - Bash

#@#,_#

a="printf '20 ';head -c21|tail -c1;exit"
b="printf '20 ';head -c21|tail -c1;exit"
[ "$a" == "$b" ]&&[ "$a" == "$b" ]&&eval "$a"

" #"

a="printf '20 ';head -c21|tail -c1;exit"
b="printf '20 ';head -c21|tail -c1;exit"
[ "$a" = "$b" ] && eval "$a"
(

Survival odds against 1 random deletion: 253/256

Survival odds against 2 random deletions: 31747/32640

Survival time against forward sequential whitespace deletion: 25

Survival time against forward sequential deletions: 10
Don't know why it prints "20 0120 " after 11 deletions when fed its own original source, it start right, but then adds a 1 and runs again.

I0AjLF8jCgphPSJwcmludGYgJzIwICc7aGVhZCAtYzIxfHRhaWwgLWMxO2V4aXQiCmI9InByaW50
ZiAnMjAgJztoZWFkIC1jMjF8dGFpbCAtYzE7ZXhpdCIKWyAiJGEiID09ICIkYiIgXSYmWyAiJGEi
ID09ICIkYiIgXSYmZXZhbCAiJGEiCgoiICMiCgphPSJwcmludGYgJzIwICc7aGVhZCAtYzIxfHRh
aWwgLWMxO2V4aXQiCmI9InByaW50ZiAnMjAgJztoZWFkIC1jMjF8dGFpbCAtYzE7ZXhpdCIKWyAi
JGEiID0gIiRiIiBdICYmIGV2YWwgIiRhIgooCg==
\$\endgroup\$
1
\$\begingroup\$

Tailstrike - Python 3

I had this laying around, doesn't seem to get immediately killed by the Burrowing Wheel. Since the last byte is quite likely to be an unnecessary line terminator, the bot actually attacks the 2nd last byte.

It turns out it's near impossible to get Python 3 to NOT use utf-8. PYTHONIOENCODING only affect the already opened stdin/stdout/stderr, open(0) defaults to utf-8 even with LC_ALL=C.ISO-8859-1.

Using sys.stdin however isn't too horrible, the default error handling is to use surrogate-escape on both stdin and stdout.

2
3
5
7



""""
11,0
13,0
'(('


import   sys

s=sys.stdin.read();print("%d "%(len(s)-2)+s[-2])
### """""


""""
"%(17,0)"
"%(19,0)"
n=s=sys.stdin.read();print("%d "%(len(s)-2)+s[-2])
###"""""

import  sys

s=sys.stdin.read();print("%d "%(len(s)-2)+s[-2])

base64 for easy copying

MgozCjUKNwoKCgoiIiIiCjExLDAKMTMsMAonKCgnCgoKaW1wb3J0ICAgc3lzCgpzPXN5cy5zdGRp
bi5yZWFkKCk7cHJpbnQoIiVkICIlKGxlbihzKS0yKStzWy0yXSkKIyMjICIiIiIiCgoKIiIiIgoi
JSgxNywwKSIKIiUoMTksMCkiCm49cz1zeXMuc3RkaW4ucmVhZCgpO3ByaW50KCIlZCAiJShsZW4o
cyktMikrc1stMl0pCiMjIyIiIiIiCgppbXBvcnQgIHN5cwoKcz1zeXMuc3RkaW4ucmVhZCgpO3By
aW50KCIlZCAiJShsZW4ocyktMikrc1stMl0pCg==
\$\endgroup\$
1
  • \$\begingroup\$ This should work on 2.7 as well. \$\endgroup\$
    – Oskar Skog
    Commented Aug 28, 2022 at 18:55
1
\$\begingroup\$

Bastarf Mark III, Python 3 only

Run with python3 bastarf.

This is a bastard, but my finger slipped. It is focused on offense targeting the current leaders.

Mark III

Implemented @mousetail's suggestion to use exec(""" to reduce sensitivity to deletions. Also made print flush as output isn't to a terminal, don't know why this worked with Mark II but maybe it didn't.

Same targets as Mark II but hopefully less fragile. Some improvements could still be made, like removing nested exec, but there already three versions.

#

#

#

#
import os
i=os.sys.stdin.read()
try:
 exec(r"""
def f(x):
 exec('p=i.find(x)\nif p>=0:print("%d %c"%(p,i[p]),flush=True);os._exit(0)')
f('=l')
f('xn')
f('_1')
f('";')
f('\n\t')
f('$(')
f('>+')
f('%c')
f('st')
1//0
""")
except:
 print("9 "+i[9])
IwoKIwoKIwoKIwppbXBvcnQgb3MKaT1vcy5zeXMuc3RkaW4ucmVhZCgpCnRyeToKIGV4ZWMociIi
IgpkZWYgZih4KToKIGV4ZWMoJ3A9aS5maW5kKHgpXG5pZiBwPj0wOnByaW50KCIlZCAlYyIlKHAs
aVtwXSksZmx1c2g9VHJ1ZSk7b3MuX2V4aXQoMCknKQpmKCc9bCcpCmYoJ3huJykKZignXzEnKQpm
KCciOycpCmYoJ1xuXHQnKQpmKCckKCcpCmYoJz4rJykKZignJWMnKQpmKCdzdCcpCjEvLzAKIiIi
KQpleGNlcHQ6CiBwcmludCgiOSAiK2lbOV0pCg==
  • f('=l') specifically targets Distributed Damage
  • f('xn') tries to target Comment Skipper by attacking the one fatal spot
  • f('_1') tries to target Almost Rare by messing up the 1-base to 0-base conversion hopefully without causing pre-mature exit
  • f('";') puts Example Python BOt in an unterminated string
  • f('\t\n') attacks Ghost
  • f('$(') will break NÜL Tränslätör, unless the input is actually cat
  • f('>+') tries to target The 2nd'er
  • f('%c') will make Example C bot output invalid data
  • f('st') targets stdin in Example Node Bot, MVP and Parenthesis Killer

And then it starts getting too big.

Mark I and Mark II are hidden in the edit history.

\$\endgroup\$
6
  • \$\begingroup\$ Is the name "Nonono, I'm gonna edit" or "bastarf"? \$\endgroup\$ Commented Aug 22, 2022 at 14:18
  • \$\begingroup\$ @mousetail: I thought I had already posted the answer and was making a last "minute" change. Anyway, there's a Mark II now. \$\endgroup\$
    – Oskar Skog
    Commented Aug 22, 2022 at 14:46
  • \$\begingroup\$ Thanks, according to my initial tests mark 2 seems significantly worse than mark 1. Will update the breadboard when I get home. \$\endgroup\$ Commented Aug 22, 2022 at 14:55
  • 1
    \$\begingroup\$ One thing you could consider is using "## at the start since many bots default to removing the first character \$\endgroup\$ Commented Aug 22, 2022 at 14:57
  • 1
    \$\begingroup\$ You could maybe use something like try:exec(""" so you don't need to indent? \$\endgroup\$ Commented Aug 22, 2022 at 15:02
1
\$\begingroup\$

The Bloody Awful, Batch

This is the most cursed I could come up with that doesn't involve having to install an Itanic emulator and learning Itanium assembler.

@@@@@@@@@@@@@@@   echo   off   

@@@@@@@@@@@@@@@   echo   off   

:::   The bloody awful   :::

::: Mmmmm         whitespace



set  /p  i=

<NUL   set  /p  x=2 %i:~2,1%

exit  /b


set  /p  i=

<NUL   set  /p  x=2 %i:~2,1%

exit  /b

It has CRLF line endings, so you may need to use the base64

QEBAQEBAQEBAQEBAQEBAICAgZWNobyAgIG9mZiAgIA0KDQpAQEBAQEBAQEBAQEBAQEAgICBlY2hv
ICAgb2ZmICAgDQoNCjo6OiAgIFRoZSBibG9vZHkgYXdmdWwgICA6OjoNCg0KOjo6IE1tbW1tICAg
ICAgICAgd2hpdGVzcGFjZQ0KDQoNCg0Kc2V0ICAvcCAgaT0NCg0KPE5VTCAgIHNldCAgL3AgIHg9
MiAlaTp+MiwxJQ0KDQpleGl0ICAvYg0KDQoNCnNldCAgL3AgIGk9DQoNCjxOVUwgICBzZXQgIC9w
ICB4PTIgJWk6fjIsMSUNCg0KZXhpdCAgL2INCg==

Installation

Wine is required. Probably just apt-get install wine. I have tested that it works with just wine64 (no wine32 installed) on Debian, but I have not tested at all on Ubuntu.

Run with wine cmd /c bloody-awful.bat

Strategy

It doesn't seem to be possible to just read a byte from "standard input" in batch/cmd. This bot will read the first line (excluding line terminator) and select one character from it. The safest character to pick would be the first on the line, but even that isn't 100% safe since the line can be completely empty.

I choose to pick the 3rd character since that seems likely enough to exist and will get to the good parts slightly faster than picking the 1st.

For hardening, there is highly redundant at symbols before the redundant echo offs as well as plenty of whitespace (including after 'off') to mitigate against attacks from comment skippers and whitespace deleters.

All critical code is duplicated but not actually redundant, but it's enough to keep the Burrowing Wheel distracted with the useless comment for a while.

And now for something completely different

A recipe for a nice smoothie

\$\endgroup\$
0
\$\begingroup\$

Random Hit, Python

###
exec( "import random
i = input()
j = random.randint(0 , len(i))
print( j + ' ' + i[j] )" )
###
exec( "import random
i = input()
j = random.randint(0 , len(i))
print( j + ' ' + i[j] )" )
###

You just need to install python, random is a built-in function. This selects a random character and removes it.


Edit

Fixed issue with quote escaping and added unnecessary whitespace to program

\$\endgroup\$
2
  • \$\begingroup\$ This doesn't work at all because you didn't escape the " \$\endgroup\$
    – emanresu A
    Commented Sep 8 at 7:26
  • \$\begingroup\$ @emanresuA fixed \$\endgroup\$ Commented Sep 8 at 8:04

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.