DEV Community

Zapwai
Zapwai

Posted on • Updated on

Not Quite a Quine

Weekly Challenge 286

Challenge 286 - My Solutions

  1. Task One Self Spammer
    Create a program that randomly prints one word of its own script / source code. (A word is anything between white-space, and every word must be printed with some positive probability.)
    e.g. 'print(" hello ")' does not work, because print(" and ") will never be displayed. (An empty script counts, but is a rather sad example.)

  2. Task Two Order Game
    You are given a list of integers whose length is a power of two. Successively select the min, then max, then min, etc. of each pair of entries.
    e.g. given (0, 5, 3, 2):
    min(0,5) = 0 & max(3,2) = 3 => new list is (0, 3):
    min(0,3) = 0 => ans: 0

Solution to task one
I thought of this question in February. In January, after I had burnt myself out on Advent of Code, I went back and was trying Perl Weekly Challenges from the very beginning - the first few weeks - from a few years before I joined. Week four, for example, had a challenge to print π to as many digits as the size of your script. One of the more beautiful challenges I have seen.

Then I was tinkering with a basic TCP server / client in Perl, and I just wanted the server to output something. A normal person would just print "Hello, World" or 'foo' or '42' or something. Spamming a word every second seemed like a nice test. So I thought: How about a script that prints a random word from itself, then I have the server call it every second?

Anyway, my intention on this challenge was simply for people to read the file, but I will be very impressed if someone takes a more quine-like approach! (A quine is a program which prints its own source - but opening a file to read the source code is considered cheating.)

P.S.
Hello jo-37! (How did you know I would be reading your script?)

After reviewing others solutions, I've noticed three or four very short ones (especially using raku) that just slurp and spit out a random word.

So far only one person treated it like a quine and did not use an open or a slurp command! Shout-out to W. Luis Mochán (wlmb) for his inspired solution. He took advantage of the somewhat weak probability constraint and made an array containing any of the symbols being used in the script. Very elegant.

(P.P.S. Tim King made a quine as well!)

Here was my first shot at a script:

open my $fh, "<$0";
my @words;
while (<$fh>) {
    push @words, split " ", $_;
}
close $fh;
print $words[int rand(@words)]."\n";
Enter fullscreen mode Exit fullscreen mode

In Javascript I continued my "just read the file" cheat by having an HTML file that asks you to upload a file, and then you can upload the HTML file itself.

I like this challenge because it's basic but still self-referential. It requires opening a file and producing a random number, which is generally useful in any language.

Fairly concise in Python:

import random
fil=open("286-1.py","r") 
words=fil.read().split()
print(words[random.randint(0,len(words)-1)])
Enter fullscreen mode Exit fullscreen mode

It inspired me to do some code golf in Perl:

open(FILE,"<$0");
$/=undef;
@words=split(" ",<FILE>);
print($words[int(rand($#words+1))]."\n");
Enter fullscreen mode Exit fullscreen mode

Only five "words"!

In my solution I did a split. But another natural solution I thought of would be to pick a random byte and then figure out what word you have landed in.

Solution to task two
The trickiest part for me was getting the bounds correct; Making sure that I covered every entry after dividing the length of the array by two. Naturally when I did this in R I forgot that the indexing begins at 1 and fought with it for a few minutes. [I'm used to Perl so I got that immediately, but other languages use the actual length rather than the convenient "length minus one" given by $#ints]

use v5.38;
use List::Util qw( min max );
my @ints = (2, 1, 4, 5, 6, 3, 0, 2);
proc(@ints);
@ints = (0, 5, 3, 2);
proc(@ints);
@ints = (9, 2, 1, 4, 5, 6, 0, 7, 3, 1, 3, 5, 7, 9, 0, 8);
proc(@ints);

sub proc(@ints) {
    say "Input: \@ints = (".join(", ", @ints).")";
    while (scalar @ints > 2) {
    my @L;
    for my $i (0 .. ($#ints - 1)/2) {
       if ($i % 2 == 0) {
        push @L, min($ints[2*$i], $ints[2*$i + 1]);
       } else {
        push @L, max($ints[2*$i], $ints[2*$i + 1]);
       }
    }
    @ints = @L;
    }
    say "Output: " . min(@ints);
}
Enter fullscreen mode Exit fullscreen mode

Top comments (1)

Collapse
 
jo37 profile image
Jörg Sommrey

Hello @zapwai, as you submitted the task, chances were above zero you would have a look at the solutions :-)