29
\$\begingroup\$

A Geiger counter is a device that is used to detect radiation.

We will be making a Geiger counter program.

As we all know, when radiation hits a computer program it removes exactly 1 byte at random. So a Geiger counter program is a program that itself does nothing, but when any byte is removed the modified program prints beep, to indicate the presence of radiation.

Answers will be scored in bytes with fewer bytes being better. Answers must be at least 1 byte.

Your program may print beep with trailing a newline or print a single newline for empty output, so long as it does so consistently. Your program may also use a different case for beep such as BEEP, bEEP, or Beep so long as it does so consistently.

\$\endgroup\$
  • \$\begingroup\$ Related, Related. \$\endgroup\$ – Wheat Wizard Aug 4 '18 at 3:18
  • 7
    \$\begingroup\$ Can we use the BEL control character to output an actual beep? \$\endgroup\$ – Jo King Aug 5 '18 at 6:47
  • 2
    \$\begingroup\$ @JoKing I toyed with the idea, it is amusing, but I have to say no. It is too substantially different. \$\endgroup\$ – Wheat Wizard Aug 5 '18 at 6:48
  • 2
    \$\begingroup\$ I want to see a solution in Retina. \$\endgroup\$ – mbomb007 Aug 7 '18 at 20:28
  • 3
    \$\begingroup\$ I'm trying to figure out how to do this in SMBF... but the only way to compare two cells involves changing them. And in SMBF, the cells you need to check are the cells the program is currently running on. So it's like the Heisenberg Uncertainty Principle. So you have to determine if anything changed using only control flow. \$\endgroup\$ – mbomb007 Aug 7 '18 at 21:39

13 Answers 13

24
+500
\$\begingroup\$

Lost, 303 293 263 253 238 228 bytes

v^"peeb"<\>"beepvv"((>@@>>%%>>(((((([[[[[[\
>>>>>>>>>//>>>>>>>>>>>>>>/>>/>>>>>>>>>>>>>\\
>>>>>>>>//>>>>\>>>>>>>>>>/>>>>>>>>>>>>>>>>>\\
>/>>>>>>>/>>>>>>>>>>>>\>>>>>>>>>>>>>>>>>>>>>\\
>>>>>>>>>>>>>>>>>>>>>>\\>>>>\>>>>>>>>>>>>>>>>\

Try it online!

Verification script (borrowed from user 202729's answer). Unfortunately this can only test half of the code at a time, but rest assured I've tested the whole program.

Ouch, this was a tough one. I'll quote WW's deleted answer:

Lost is perhaps the most interesting language for this challenge. In Lost the start location and direction of the pointer is entirely random, thus to make deterministic programs you must account for every possible start location and direction. At the same time, by the nature of this challenge you must also account for any single byte being removed.

Unfortunately, his answer didn't take into account removing newlines, which screwed everything up.

Explanation:

(note that a few bytes might be off here and there)

First let's talk about the general structure of the code:

v^^"peeb"<<\/"beepvv"((>>>@@>>%%>>(((((([[[[[[[\       Processing line
>>>>>>>>>>>//>>>>>>>>>>>>>>>>>>>/>>>>>>>>>>>>>>\\      Beep line
>>>>>>>>>//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\\     Back-up beep line
>//>>>>>>>>>>>>>>>>>>>>\\>>>>>>>>>>>>>>>>>>>>>>>>\\    Back-up return line
>>>>>>>>>>>>>>>>>>>>>>>>\\>>>>>>\>>>>>>>>>>>>>>>>>\    Return line

Everything but the processing line must be entirely composed of either > or one of \/. Why? Well, as an example, let's remove a newline:

v^^"peeb"<<\/"beepvv"((>>>@@>>%%>>(((((([[[[[[[\>>>>>>>>>>>//>>>>>>>>>>>>>>>>>>>/>>>>>>>>>>>>>>\\
>>>>>>>>>//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\\
>//>>>>>>>>>>>>>>>>>>>>\\>>>>>>>>>>>>>>>>>>>>>>>>\\
>>>>>>>>>>>>>>>>>>>>>>>>\\>>>>>>\>>>>>>>>>>>>>>>>>\

The first line is now way longer than the rest of the block. If a pointer were to spawn on a non->\/ character with vertical movement, then it would get stuck in an infinite loop.


The biggest radiation detector part is the section at the end of each line.

 \
 \\
 >\\
 >>\\
 >>>\

Normally an IP passing through this from the first line would exit out the last line. However, if any character on the line is removed, then that line shifts down one, e.g:

 \
 \\
 >\\
 >\\
 >>>\

And the IP instead exits out the line that is missing a byte (with the exception of the last line, where it exits out the second to last).

From there, each of the first four lines will redirect to the second line:

v
>>>>>>>>>>
>>>>>>>>//
>/

Which will then lead into either one of the two beepers.

v^"peeb"<<\/"beepvv"((>
>>>>>>>>>>//

If any of the bytes in the first beeper have been removed, then it instead goes to the second:

v^^"peb"<<\/"beepvv"((>
>>>>>>>>>>>//

Both beepers then lead back to the first line and the terminating @.

Some other miscellaneous parts:

The (((((([[[[[[[ is used to clear the stack when the pointer starts inside a pair of quotes and ends up pushing the whole first line to stack. It has to be so unfortunately long because the first newline can be removed to make the first line twice the size. Experimenting in generating the beep using arithmetic instead of quotes ended up longer.

The \s and /s scattered across the lines are there to golf bytes in the top line of code by redirecting the pointer to the correct lines. As most of the bottom lines are just filler, only the top line is able to be golfed. If anyone has any ideas for a shorter radiation proof stack clearer then what I've got now, feel free to comment.

\$\endgroup\$
  • \$\begingroup\$ Just out of curiosity, how helpful was the partial answer I posted in chat? I saw some similarities in the earlier versions, and I'd like to know if I was on the right track. \$\endgroup\$ – Wheat Wizard Aug 7 '18 at 5:31
  • \$\begingroup\$ @WW I had already been working on it by then, but the \/ to separate the beep pushes and the fact that only one of the quotes needed an exit clause helped \$\endgroup\$ – Jo King Aug 7 '18 at 5:36
20
\$\begingroup\$

Hexagony, 38 bytes

.....;p;<>b;e;/<b;e;;p...@@.......;@..

Try it online!

Verification program.


Explanation

We make use of Hexagony's auto-detection of hexagon side-length here.

If no bytes are removed, the program has side length 4 and looks like this:

Program without any bytes removed

If however, a byte is removed. There are 2 cases.

  1. The removed byte is after the second <.

    The execution flow would be:

    Program with last byte removed

    There are 2 consecutive @ on the 5th line, so even if one of them are removed, the IP will safely hit a @.

  2. The removed byte is at or before the second <.

    Then the second half will stay intact, and the IP is no longer redirected upwards by that <. Image of execution flow:

    Program with the second <code><</code> removed

\$\endgroup\$
19
\$\begingroup\$

Hexagony, 34 29 bytes

//..>;e;<b@;p;/|/;e;;\.b@;p<@

Try it online! Verification!

Explanation:

Here is the normal code formatted into a proper hexagon using HexagonyColorer:

No Cancer...

The double // at the beginning ensures that this path is always taken. If any character is removed, the @ is removed from the path, either being shifted one back or being removed itself:

Cancer!

In this case, we've removed a character after the |, which makes it follow this path, printing beep:

First beep

If we instead remove a character from before the | (or the | itself), we follow the other beep printer:

Second beep

We've then accounted for all possibilities and we only beep using the un-irradiated parts of the program.

\$\endgroup\$
13
+500
\$\begingroup\$

Self-modifying Brainfuck, 73 63 bytes

<<[[[[<<]]>[[.>>..>>.[,>]]]]   bbeepp+[<<<]>>[[>]>>>.>>..>>.,+]

Try it online! Verification!

The spaces in the middle of the code are actually representing NUL bytes.

Explanation:

The code is split into two sections by 3 NUL bytes in the middle. Both of them basically prints beep if the other section is irradiated (with a couple of exceptions).

First, the <<[[ at the beginning is to ensure that all ]s are matched at any time. [s won't attempt to look for a matching ] if the cell is positive, while ]s do. If any ] jumps back to one of these brackets it usually jumps back straight away because the cell is 0.

The next part, [[<<]]> then checks if the length of section 2 is even. If so, it executes the other half of section 1, which prints beep using the bbeepp at the start of section 2.

[[.>>..>>.[,>]]]]

It then clears all of section 2 so it doesn't execute.

In section 2, we check if the length of section 1 and the NUL bytes is divisible by 3 with +[<<<]>>.

[[>]>>>.>>..>>.,+]

Similarly, we print beep.

\$\endgroup\$
10
\$\begingroup\$

Z80Golf, 53 36 34 bytes

-16 bytes thanks to @Lynn
-2 byte thanks to @Neil

Since this is just Z80 machine code, there is a lot of unprintables in this one, so have a xxd -r-reversible hexdump:

00000000: ddb6 2120 10dd b615 280c 003e 62ff 3e65  ..! ....(..>b.>e
00000010: ffff 3e70 ff76 003e 62ff 3e65 ffff 3e70  ..>p.v.>b.>e..>p
00000020: ff76                                     .v

Try it online! (exhaustive tester in Python)

Explanation

z80golf is Anarchy Golf's hypothetical Z80 machine, where call $8000 is a putchar, call $8003 is a getchar, halt makes the interpreter exit, your program is placed at $0000, and all other memory is filled with zeroes. Making programs radiation-proof in assembly is pretty hard, but a generically useful technique is using one-byte idempotent instructions. For example,

or c        ; b1    ; a = a | c

is just one byte, and a | c | c == a | c, so it can be made radiation-proof by just repeating the instruction. On the Z80, an 8-bit immediate load is two bytes (where the immediate is in the second byte), so you can load some values into registers reliably too. This is what I originally did at the beginning of the program, so you can analyze the longer variants I archived at the bottom of the answer, but then I realized that there is a simpler way.

The program consists of two independent payloads, where one of them could have been damaged by radiation. I check whether a byte was removed, and whether the removed byte was before the second copy of the payload, by checking the values of some absolute memory addresses.

First, we need to exit if no radiation was observed:

    or a, (ix+endbyte) ; dd b6 21 ; a |= memory[ix+0x0021]
    jr nz, midbyte     ; 20 10    ; jump to a halt instruction if not zero

If any byte was removed, then all the bytes will shift and $0020 will contain the last 76, so $0021 will be a zero. We can afford radiating the beginning of the program, even though there is virtually no redundancy:

  • If the jump offset $10 is removed, then radiation will correctly be detected, the jump will not be taken, and the offset will not matter. The first byte of the next instruction will be consumed, but since it's designed to be resistant to byte removals, this doesn't matter.
  • If the jump opcode $20 is removed, then the jump offset $10 will decode as djnz $ffe4 (consuming the next instruction byte as the offset -- see above), which is a loop instruction -- decrement B, and jump if the result is not zero. Because ffe4-ffff is filled with zeroes (nops), and the program counter wraps around, this will run the beginning of the program 256 times, and then finally continue. I am amazed this works.
  • Removing the $dd will make the rest of the snippet decode as or (hl) / ld ($1020), hl, and then slide into the next part of the program. The or will not change any important registers, and because HL is zero at this point, the write will also cancel out.
  • Removing the $b6 will make the rest decode as ld ($1020), ix and proceed as above.
  • Removing the $21 will make the decoder eat the $20, triggering the djnz behavior.

Note that using or a, (ix+*) saves two bytes over ld a, (**) / and a / and a thanks to the integrated check for zero.

We now need to decide which of the two copies of the payload to execute:

    or (ix+midbyte)  ; dd b6 15
    jr z, otherimpl  ; 28 0c
    nop              ; 00
    ; first payload
    ld a, 'b'        ; 3e 62
    rst $0038        ; ff
    ld a, 'e'        ; 3e 65
    rst $0038        ; ff
    rst $0038        ; ff
    ld a, 'p'        ; 3e 70
    rst $0038        ; ff
midbyte:
    halt             ; 76
otherimpl:
    nop              ; 00
    ld a, 'b'        ; 3e 62
    ; ...            ; ...
    rst $0038        ; ff
endbyte:
    halt             ; 76

The two copies are separated by a nop, since a relative jump is used to choose between them, and radiation could have shifted the program in a way that would make the jump skip the first byte after the destination. Additionally, the nop is encoded as a zero, which makes it easy to detect shifted bytes. Note that it doesn't matter which payload is chosen if the switch itself is corrupted, because then both copies are safe. Let's make sure that it will not jump into uninitialized memory, though:

  • Deleting $dd will make the next two bytes decode as or (hl) / dec d. Clobbers D. No big deal.
  • Deleting $b6 will create an undocumented longer encoding for dec d. Same as above.
  • Deleting $15 will read the $28 instead as the offset, and execution will proceed at the $0c, as below.
  • When $28 disappears, the $0c is decoded as inc c. The payload does not care about c.
  • Deleting $0c - that's what the nop is for. Otherwise, the first byte of the payload would have been read as the jump offset, and the program would jump into uninitialized memory.

The payload itself is pretty simple. I think the small size of the string makes this approach smaller than a loop, and it's easier to make position-independent this way. The e in beep repeats, so I can shave off one ld a. Also, because all memory between $0038 and $8000 is zeroed, I can fall through it and use a shorter rst variant of the call instruction, which only works for $0, $8, $10 and so on, up to $38.

Older approaches

64 bytes

00000000: 2e3f 3f2e 3f3f 7e7e a7a7 201f 1e2b 2b1e  .??.??~~.. ..++.
00000010: 2b2b 6b00 7ea7 2814 003e 62cd 0080 3e65  ++k.~.(..>b...>e
00000020: cd00 80cd 0080 3e70 cd00 8076 003e 62cd  ......>p...v.>b.
00000030: 0080 3e65 cd00 80cd 0080 3e70 cd00 8076  ..>e......>p...v

58 bytes

00000000: 2e39 392e 3939 7e7e a7a7 2019 3a25 00a7  .99.99~~.. .:%..
00000010: 2814 003e 62cd 0080 3e65 cd00 80cd 0080  (..>b...>e......
00000020: 3e70 cd00 8076 003e 62cd 0080 3e65 cd00  >p...v.>b...>e..
00000030: 80cd 0080 3e70 cd00 8076                 ....>p...v

53 bytes

This one has an explanation in the edit history, but it's not too different.

00000000: 3a34 00a7 a720 193a 2000 a728 1400 3e62  :4... .: ..(..>b
00000010: cd00 803e 65cd 0080 cd00 803e 70cd 0080  ...>e......>p...
00000020: 7600 3e62 cd00 803e 65cd 0080 cd00 803e  v.>b...>e......>
00000030: 70cd 0080 76                             p...v

What if: any non-empty output was fine instead of beep

1 byte

v

halts the program normally, but if radiation removes it, then the memory will be full of zeroes, making $8000 execute an infinite number of times, printing a lot of null bytes.

\$\endgroup\$
  • \$\begingroup\$ Since a starts at zero, can you not use or a, (N); instead of ld a, (N); and a;? It looks as if you can save a couple of bytes that way. \$\endgroup\$ – Neil Aug 8 '18 at 8:35
  • \$\begingroup\$ @Neil Good question! Unfortunately, on the Z80, only load instructions can take addresses like this. \$\endgroup\$ – NieDzejkob Aug 8 '18 at 11:50
  • \$\begingroup\$ Ugh, it's been too long since I did any Z80 programming... maybe I was thinking of or a, (ix + N)? \$\endgroup\$ – Neil Aug 8 '18 at 15:17
  • \$\begingroup\$ @Neil actually, that exists, and IX starts at zero too... unfortunately, saving a byte in that area makes the bytes shift in such a way that the 20 19 at the beginning becomes 20 18, and removing the 20 creates an unconditional jump backwards, so a nop has to be added after the first jump in the program, reversing the byte save. \$\endgroup\$ – NieDzejkob Aug 8 '18 at 17:55
  • \$\begingroup\$ Ah, that's a shame. Thanks for checking though! \$\endgroup\$ – Neil Aug 8 '18 at 19:10
8
\$\begingroup\$

><>, 23 bytes

<|o<"beep"/
>o<"beep"<\

Try it online! Verification.

It's honestly really surprising how short I got this. Ends with an error.

Non-erroring version, 29 bytes

<<;oooo"beep"/
 ;oooo"beep"<\

Try it online!

\$\endgroup\$
4
\$\begingroup\$

Klein, one of each topology, totaling 291 bytes

After seeing WW's answer using the 001 topology, I decided to see how hard it would be to do a Geiger Counter for each topology. (Spoiler: very hard. It's difficult to figure out where the pointer will go without hand gestures that make me look like I'm figuring out which hand is my left)

Verification!

(I also thought about writing a program that is a valid Geiger counter on all topologies, but that might have to wait. If anyone else wants to try though, I'm offering a 500 rep bounty)

000 and 010, 21 bytes

<<@"peeb"/
.@"peeb"<\

Try 000 online! and Try 010 online!

This is ported from my ><> solution. This obviously works in 000, since that's the default topology for most 2D languages, but I was surprised that it also works in 010.

001 and 011, 26 bytes

!.<<@"peeb"/
.@"peeb"..<..

Try 001 online! and Try 011 online!

This one is copied directly from WW's answer. Thanks!

100, 21 bytes

//@"peeb"\
@"peeb".</

Try it online!

101, 21 bytes

//@"peeb"/
@"peeb".<!

Try it online!

110, 26 bytes

<.<@"peeb"\\
.\@."peeb".\<

Try it online!

111, 24 bytes

<<@"peeb"<\
...@"peeb"//

Try it online!

200, 21 bytes

<<@"peeb"\
@"peeb".!/

Try it online!

201, 31 bytes

\\.\.@"peeb"</./
./...@"peeb"<\

Try it online!

By far the most annoying.

210, 26 bytes

/\\@"peeb"</\
/@.."peeb"<\

Try it online!

211, 27 bytes

\\."peeb"((</
!/@@<"peeb"<\

Try it online!

The only one where I had to handle entering the beeper through the right side.

\$\endgroup\$
  • \$\begingroup\$ I'll happily second that bounty. \$\endgroup\$ – Wheat Wizard Aug 21 '18 at 12:50
3
\$\begingroup\$

Self-modifying Brainfuck, 144 102 bytes

Unprintables are displayed as escape sequence (for example \x01).

\xa8<<[[<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<[[[.<-]>[>-]\x01qffb\x00\x00beep\x00]]]<[>]<<[>[-<+>]+<<]>[[>]>>[.>]]\x01

Verification!

\$\endgroup\$
2
\$\begingroup\$

Runic Enchantments, 29 bytes

>>yyLL@"peeb"/
     @"peeb"L\

Try it online!

Essentially the same as the Klein 000 answer or ><> answer (I started with the Klein one). The only change really needed was to turn the < into L and . into  (translation of command symbols), inserting the IP entry points (need 2, otherwise a deletion would result in a non-compiling program) and the insertion of the delay command to get the two IPs to merge (thus printing only one beep), again, needing two. Also required inserting additional NOPs to keep line lengths the same. Klein conveniently also uses @ for "print and terminate."

No ability to utilize the whitespace in the lower left, as any reflectors to change direction inhibits the ability to detect radiation. e.g. (26 bytes, irradiated y):

/yLL@"peeb"/
\<<  @"peeb"L\

Prints no output, due to the bent entry segment causing a re-reflection back to the lower line's terminator.

\$\endgroup\$
1
\$\begingroup\$

Befunge-93, 55 bytes

<< >       vv
>,,@#"beep"<"
v  |   _ #-<_
>"peeb",,,,@>

Try it online! Verification!

I'd hoped to get it a little smaller than this, but the length of the beep printer is the bottleneck.

\$\endgroup\$
1
\$\begingroup\$

Wumpus, 37 34 32 31 bytes

777*7..@ $o&4"beep"|"@peeb"4&o@

Try it online! Verification!

This solution uses the fact that . jumps to a position modulus the length of the program.

Alternatively for the same amount of bytes


" @o&4"beep"}@
@o&4"beep"}$}  

Try it online! Verification!

This one utilises the difference in the direction of the pointer for odd and even line lengths. (I don't really know how the first " actually works when the newline is removed)

\$\endgroup\$
1
\$\begingroup\$

Klein (001), 26 bytes

!.<<@"peeb"/
.@"peeb"..<..

Try it online!

Verify!

Explanation

This program takes advantage of Klein's unique topology in particular the 001 topology, which is a Klein bottle.

Unedited the program follows the execution path:

Orange path

Removing a byte from the program can effect the program in 4 ways (each in a different color):

Program sections

The first thing to note is that << will always deflect the ip to the left of the origin at the start. If one of the <s is deleted the other takes its place. So if any byte is removed from the red section the following execution path will be followed:

Red path

If the blue byte is removed we get the very simple path:

enter image description here

If the newline is removed we get the path:

Green path

The yellow path is a bit more complex. Since the bottom line is one longer than the top line, when the program is squared at the beginning of execution a virtual character is added to the end of the first line to make them the same size. If any byte on the second line is removed the line is shortened and that virtual character is not added. This is important because ! normally jumps over the virtual character, but in its absence it jumps over / instead.

Yellow path

\$\endgroup\$
  • 1
    \$\begingroup\$ You could port my ><> solution in 000 for 21 bytes \$\endgroup\$ – Jo King Aug 18 '18 at 3:07
  • \$\begingroup\$ @JoKing I think that would be better as its own answer. \$\endgroup\$ – Wheat Wizard Aug 18 '18 at 3:35
1
\$\begingroup\$

Backhand, 25 21 bytes

vv""ppeeeebb""jjHH@

Try it online! Verification!

This uses Backhand's ability to change the pointer step value to skip an instruction every step and neatly solve the redunancy issue. It then uses the j command to check if the code is irradiated by jumping to the last character (@, halt) if not, and jumping to the second last (H, halt and output stack) if so.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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