75
\$\begingroup\$

Richard Dawkins in his book The Blind Watchmaker, describes a Weasel program. The algorithm can be described as follows:

  1. Start with a random string of 28 characters. Valid characters are all upppercase letters, and space.

  2. Make 100 copies of that string, with a 5% chance per character of that character being replaced with a random character.

  3. Compare each new string with the target "METHINKS IT IS LIKE A WEASEL", and give each a score according to the number of letters in the string which are correct and in the correct position.

  4. If any of the new strings has a perfect score (28), halt.

  5. Choose the highest-scoring string from step 3. How you work out a tie is up to you, but only one string may be chosen. Take the chosen string and go to step 2.

The winner will be the shortest code snippet to get to the correct answer while printing the highest-scoring string of each generation in the following format:

answers in this format please

If people could help by checking other peoples answers would be very helpful!

\$\endgroup\$
19
  • 5
    \$\begingroup\$ Which characters are allowed? Unicode? Lowercase? \$\endgroup\$
    – Oriol
    Commented Jan 3, 2014 at 17:42
  • 5
    \$\begingroup\$ Ah, I love Dawkins. Beauty, and feasibility of evolution shown in a simple algorithm. \$\endgroup\$
    – Cruncher
    Commented Jan 3, 2014 at 18:16
  • \$\begingroup\$ May step 4 be better replaced with "step 1.5) If the new strings has a perfect score (28), halt" and "step 4) Take the highest scoring string, and go to step 1.5."? That is, if the initial random string is a winner, need we fan out? \$\endgroup\$ Commented Jan 3, 2014 at 18:21
  • 2
    \$\begingroup\$ I'm a bit confused as to the order of operations here. Is the intent that we make 100 new strings, based on the original string? Or is it 100 new strings, with the first string being based on the original, and each subsequent string based on the previous string? The algorithm description seems to imply the former, while the sample output appears to be of the latter. \$\endgroup\$
    – Iszi
    Commented Jan 6, 2014 at 19:18
  • 2
    \$\begingroup\$ The instructions are pretty clear, but what if the original string is the target? \$\endgroup\$ Commented Jan 8, 2014 at 15:41

47 Answers 47

29
+100
\$\begingroup\$

APL (143)

0{⍵≢T←'METHINKS IT IS LIKE A WEASEL':c∇⍨1+⍺⊣⎕←(⍕⍺),':'c'-- score:',s⊣c←⊃c/⍨G=s←⌈/G←{+/⍵=T}¨c←{⍵{⍵:⍺⋄C[?27]}¨9≠?28/20}¨100⍴⊂⍵}⊃∘(C←27↑⎕A)¨?28/27

Explanation:

  • 0{...}⊃∘(C←27↑⎕A)¨?28/27: set C to the first 27 capital letters. There are only 26, so the 27th element will be a space. Select 28 random items from C. This will be the first . The first (generation) will be 0.

  • ⍵≢T←'METHINKS IT IS LIKE A WEASEL: set T to the string 'METHINKS IT IS LIKE A WEASEL'. As long as is not equal to T:

    • {...}¨100⍴⊂⍵: Make 100 copies of . For each of these...
      • 9≠?28/20: select 28 random numbers from 1 to 20. Make a bitmask where each 1 means that the random number was not equal to 9. (This means 5% chance of a 0).
      • ⍵{⍵:⍺⋄C[?27]}¨: for each letter in , if the corresponding bit was 1, keep that letter, otherwise replace it with a randomly chosen element from C.
    • c←: store the 100 mutated strings in c.
    • G←{+/⍵=T}¨c: for each element in c, calculate the score (amount of characters that match T) and store the scores in G.
    • s←⌈/G: find the maximum score and store that in s.
    • c←⊃c/⍨G=s: select the first item from c whose score is equal to s (the maximum), and store it in c again.
    • ⎕←(⍕⍺),':'c'-- score:',s: print the generation in the given format ( is current generation, c is current best string, s is score)
    • c∇⍨1+⍺: Increment the generation and run the mutation again using the current best string (c) as input.
\$\endgroup\$
5
  • 5
    \$\begingroup\$ Explanation? This looks like alphabet soup! :) \$\endgroup\$ Commented Jan 7, 2014 at 16:46
  • 2
    \$\begingroup\$ @ToastyMallows: added explanation \$\endgroup\$
    – marinus
    Commented Jan 7, 2014 at 17:20
  • 1
    \$\begingroup\$ Best answer so far, great to have an explaination with it. \$\endgroup\$
    – Noelkd
    Commented Jan 7, 2014 at 17:22
  • 1
    \$\begingroup\$ @marinus Are you a wizard? \$\endgroup\$ Commented Jan 8, 2014 at 13:52
  • 3
    \$\begingroup\$ Bullcrap, you just opened MS Word and used WingDings font \$\endgroup\$ Commented Jan 9, 2014 at 22:04
11
\$\begingroup\$

Mathematica - 238 236 225

c:="@"~CharacterRange~"Z"~RandomChoice~28/."@"->" "
For[s=""<>c;i=0,{d,s}=Sort[{#~HammingDistance~"METHINKS IT IS LIKE A WEASEL",#}&@
StringReplace[s,_/;20Random[]<1:>c〚1〛]&~Array~100]〚1〛;
d>0Print[i++,":"s," -- score: ",28-d],]

Example output

0:  CYPMEIHADXRXVTFHERYOZNRVFCSQ  -- score: 0
1:  CYPMEIHADIRXVTFBERYOZNRVFCSQ  -- score: 1
2:  CYPMEIHA IRXVTFBIRYOZNRVFCSQ  -- score: 3
...
50:  METHINKS IT IS LIKE A WEASEL  -- score: 28
\$\endgroup\$
10
\$\begingroup\$

Python 2, 272 bytes

from random import choice as c
n=range
a=map(chr,n(65,91)+[32])
s=map(c,[a]*28)
p=x=0
while p<28:
 p,s=max((sum(g==r for g,r in zip(y,'METHINKS IT IS LIKE A WEASEL')),y)for y in ([c(a+[x]*513)for x in s]for _ in n(100)));print '%d: %s -- score: %d' % (x,''.join(s),p);x+=1

Try it online!

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

K, 173 167

o:"METHINKS IT IS LIKE A WEASEL"
i:0;{~x~o}{-1($i),": ",(r:a@*&b=c)," -- score: ",$c:max@b:+/'o=/:a:{x{if[0~*1?20;x[y]:*1?s];x}/!#x}'100#,x;i+:1;r}/28?s:"c"$32,65+!26;

/

0: FQRZPHACDIBHZOUUCYKKFBJWVNVI -- score: 1
1: FQRZP ACDITHCOUUCYKKFBJWVNVI -- score: 2
2: FQRZP AFDIT COUUCYKKFBJWVNVI -- score: 3
...
51: METHINKS IT IS LIKECA WEASEL -- score: 27
52: METHINKS IT IS LIKECA WEASEL -- score: 27
53: METHINKS IT IS LIKE A WEASEL -- score: 28
\$\endgroup\$
7
\$\begingroup\$

R (245 239 238 characters)

t=strsplit("METHINKS IT IS LIKE A WEASEL","")[[1]]
h=sample
s=h(f<-c(LETTERS," "),28,T)
c=0
while(!all(s==t)){for(i in 1:100){z=ifelse(runif(28)<.05,h(f,1),s)
y=sum(s==t)
if(sum(z==t)>y)s=z}
cat(c<-c+1,": ",s," -- score: ",y,"\n",sep="")}

Gives:

1: HSSSIMJM ETJISGBSCIELUYPLSED -- score: 7
2: HSSSIMJM ETJISGBSKIELUYPLSED -- score: 8
3: EETLITLM ETJISTBSKIELUYLLSEL -- score: 11

...

78: METHINKS IT IS LIKEEA WEASEL -- score: 27
79: METHINKS IT IS LIKEEA WEASEL -- score: 27
80: METHINKS IT IS LIKEEA WEASEL -- score: 27
81: METHINKS IT IS LIKE A WEASEL -- score: 28
\$\endgroup\$
3
  • \$\begingroup\$ how to you get your first line 0: ... if the first time you invoke cat you increment c to 1? (+1 nonetheless as i m trying since an hour to do something shorter and i still can't :) ) \$\endgroup\$
    – plannapus
    Commented Jan 8, 2014 at 14:20
  • \$\begingroup\$ @plannapus That is true, was from an old version (which was somewhat longer). Either I change it to -1 in the beginning, or use 1 in the first line... \$\endgroup\$
    – Henrik
    Commented Jan 8, 2014 at 22:34
  • \$\begingroup\$ I see two problems with your code. First, this ifelse(…,h(f,1),…) replaces all selected positions with the same random char. You can interpret the rules in this direction, but it feels like bending them so I'd at least mention it. Second, you replace s=z within the 1:100 loop, so you are not making 100 copies of the same string, but sometimes copies of a copy. This seems like breaking a rule to mee, not merely bending it. \$\endgroup\$
    – MvG
    Commented Jan 9, 2014 at 22:43
6
\$\begingroup\$

Python: 282 characters no semi colons

from random import*
g,r,l,c=0,0,"ABCDEFGHIJKLMNOPQRSTUVWXYZ ",choice
k=map(c,[l]*28)
while(r!=28):
 r,k=max((sum(i==j for i,j in zip(t,"METHINKS IT IS LIKE A WEASEL")),t)for t in[[c(l)if random()<.05 else i for i in k]for i in[1]*100])
 print`g`+":","".join(k),"-- score:",`r`
 g+=1

278 with:

from random import*;g,r,l,c=0,0,"ABCDEFGHIJKLMNOPQRSTUVWXYZ ",choice;k=map(c,[l]*28)
while(r!=28):r,k=max((sum(i==j for i,j in zip(t,"METHINKS IT IS LIKE A WEASEL")),t)for t in[[c(l)if random()<.05 else i for i in k]for i in[1]*100]);print`g`+":","".join(k),"-- score:",`r`;g+=1
\$\endgroup\$
11
  • 4
    \$\begingroup\$ You should explicitly state the language and since the question is tagged code-golf, you should provide a character count. \$\endgroup\$ Commented Jan 3, 2014 at 12:27
  • \$\begingroup\$ Will do, thanks for the tips tim. \$\endgroup\$
    – Noelkd
    Commented Jan 3, 2014 at 12:47
  • \$\begingroup\$ Also, you should use all one letter variable names to decrease the character count further. \$\endgroup\$
    – Doorknob
    Commented Jan 3, 2014 at 14:40
  • \$\begingroup\$ Waiting till later to golf it down some more, still got some easy wins. Thanks. \$\endgroup\$
    – Noelkd
    Commented Jan 3, 2014 at 14:45
  • 1
    \$\begingroup\$ Is it really supposed to start with all same letters? :) \$\endgroup\$ Commented Jan 7, 2014 at 18:51
5
\$\begingroup\$

JavaScript, 277 246

c=m=>" ABCDEFGHIJKLMNOPQRSTUVWXYZ"[0|Math.random()*m];for(s=[k=28];e=k;s[--k]=c(27));for(;alert(k+++": "+s.join("")+" -- score: "+e),e<28;s=t)for(z=100;f=0,z--;f>e&&(t=n,e=f))n=s.map((h,p)=>(h=c(540)||h,f+=h=="METHINKS IT IS LIKE A WEASEL"[p],h))

(requires arrow function support; indentation added only for readability)

// c() returns a random char using `m` as an index max
c=m=>" ABCDEFGHIJKLMNOPQRSTUVWXYZ"[0|Math.random()*m];

// generate base string `s`
for(s=[k=28];e=k;s[--k]=c(27));

// while score `e` is < 28
for(;alert(k+++": "+s.join("")+" -- score: "+e),e<28;s=t)
    for(z=100;f=0,z--;f>e&&(t=n,e=f))            // do 100 mutations; keep best score
        n=s.map((h,p)=>(                         // map `s` to `n` with 5% mutation
            h=c(540)||h,                         // change the char in 5% of cases
            f+=h=="METHINKS IT IS LIKE A WEASEL"[p],  // score++ if char matches
            h                                    // arrow function return character
        ))

Feel free to change alert to console.log if you want a more pleasant execution experience.

There are some nifty golfy bits in here:

  • The function c returns a random character from the alphabet string " ABC...". The function takes an argument to use as an upper bound for the random index selection. When generating the base string, we use 27, so the function behaves normally.

    However, we abuse this behavior by asking for a random upper bound of 540 in h = c(540) || h. Only 5% of the time will c actually return a string (because 540 * .05 = 27); the other 95% of the time, the randomly-chosen index falls beyond the length of the string, so the function returns undefined. This falsey value causes a logical-OR cascade in c(540) || h, so the original map value h is used (i.e., no replacement occurs).

  • The score-summing operation does f+=h=="METHINKS IT IS LIKE A WEASEL"[p], which says "add true to f if the current map character h matches the pth character of the WEASEL string". The number-plus-boolean addition coerces the boolean result to either 0 or 1, which means that f is incremented only when there is a match against the target WEASEL string.

\$\endgroup\$
4
  • \$\begingroup\$ Why have you got v stated in the code? It's not mentioned anywhere else in there. You can save yourself 2 chars.? \$\endgroup\$ Commented Jan 7, 2014 at 1:24
  • 1
    \$\begingroup\$ @Eliseod'Annunzio v is an argument to the arrow function stored in c: c = (v => ...). If you want to define an arrow function without arguments, it costs two characters, ()=>... , instead of one, v=>..., so it's better to simply have an unused argument. \$\endgroup\$
    – apsillers
    Commented Jan 7, 2014 at 1:26
  • \$\begingroup\$ Clever use of code! \$\endgroup\$ Commented Jan 7, 2014 at 1:29
  • \$\begingroup\$ Nice trick with k=s=[28] and ++, I had no idea! \$\endgroup\$ Commented Jan 7, 2014 at 6:16
5
\$\begingroup\$

C 256

char c[101][29],s,n,z,b,j,i,w;g;main(){for(;w<28;printf("%d: %s -- score: %d\n",g++,c[b=n],w))for(i=w=0;i<101;i++)for(s=j=0;j<28&&!(i==b&&g);j++)(s+=(c[i][j]=g&&rand()%20?c[b][j]:(z=rand()%27)?'A'+z-1:' ')=="METHINKS IT IS LIKE A WEASEL"[j])>w?n=i,w=s:0;}

Simple three loops, initialization, generation of new strings from parent and score calculated by the same statement. It's not very readable even with indentation.

C 252

i,g,n,b,o,s,w,z;char c[2929];main(){for(;(o=i%29)|i|w<28;(i=(i+1)%2929)||printf("%d: %s -- score: %d\n",g++,&c[b=n],w))(s+=o>27?-s:((i-o!=b||!g)&&(c[i]=g&&rand()%20?c[b+o]:(z=rand()%27)?'A'+z-1:' ')=="METHINKS IT IS LIKE A WEASEL"[o]))>w?n=i-o,w=s:0;}

One loop, with one array holding all 101 strings.

This second version breaks the rules because it prints the string from (the equivalent of) step 1, but it was either that or not print the last string. I'm stumped how to fix it without exploding in size. I'm posting it anyway for inspiration.

C 256

struct{char d[29];}p,t,n;i,j=-1,z,s,w,g;main(){for(;w<28;j>1&&printf("%d: %s -- score: %d\n",g++,(p=n).d,w))for(;j++%100;p=j?p:t)for(s=0,i=28;i--;)(s+=(t.d[i]=j&&rand()%20?p.d[i]:(z=rand()%27)?'A'+z-1:' ')=="METHINKS IT IS LIKE A WEASEL"[i])>w?n=t,w=s:0;}

Different approach, instead of making an array to hold 101 strings just regenerate the string 100 times and use struct assignment for easy copying. Initialization is done by starting the "repeat 100 times" counter at -1 and handling it carefully by strategically chosen post-increment. Despite a very different approach it ends up exactly the same as the first attempt - 256 characters.

\$\endgroup\$
5
\$\begingroup\$

Japt -R, 94 91 90 89 bytes

Been trying to break 94 for years, and I finally did it :D

A different approach to, but (still) with a little inspiration from, Bubbler's solution.

;B±S ¬ö28
@=#dÆ£BùX540 öÃÃñèV o)ʶG}a@NpX+`: {¬} -- sÖ: {G=UèV=ÈoYg`Ú0ks   ¦ke a Øâel

Test it (Warning: can be slow as hell!)


Explanation

;B±S ¬ö28
;B            :Uppercase alphabet
  ±           :Append and reassign result to variable B
   S          :  Space
     ¬        :Split
      ö28     :Generate an array of 28 random characters
              :Assign that array to variable U

@=#dÆ£BùX540 öÃÃñèV o)ʶG}a@NpX+`: {¬}...{G=UèV=ÈoYg`...
@                                                            :Left function
 =                                                           :  Reassign to U
  #d                                                         :    100
    Æ                                                        :    Map the range [0,100)
     £                                                       :      Map each X in U
      BùX540                                                 :        Left pad B with X to length 540
             ö                                               :        Select a random character
              Ã                                              :      End inner map
               Ã                                             :    End outer map
                ñ                                            :    Sort by
                 èV                                          :      Count of elements that return truthy (non-empty string) when passed through function V (see below)
                    o                                        :    Pop the last element
                     )                                       :  End reassignment
                      Ê                                      :  Length
                       ¶G                                    :  Is equal to variable G (see below)
                         }                                   :End left function
                          a                                  :Repeatedly run the right function then the left until the latter returns true
                           @                                 :Right function
                            N                                :  Array of all inputs (initially empty)
                             p                               :  Push
                              X+                             :    Append to the current 0-based iteration index
                                `                            :    Compressed string
                                 :                           :      Literals
                                   {                         :      Interpolate
                                    ¬                        :        U joined to a string
                                     }                       :      End interpolate
                                      ...                    :      Compressed " -- score: "
                                         {                   :      Interpolate
                                          G=                 :        Assign to variable G
                                            Uè               :        Count of elements in U that return truthy
                                              V=             :          Assign to variable V
                                                È            :          Function taking a character string (X) and an index (Y) as arguments
                                                 o           :            Case insensitively keep the characters in X that appear in
                                                  Yg         :              Index Y into
                                                    `...     :                Compressed string "methinks it is like a weasel"
                                                             :Implicit output of N joined with newlines
\$\endgroup\$
4
\$\begingroup\$

C# - 436

namespace System.Linq{class W{static void Main(){var r=new Random();
Func<char>c=()=>(char)(r.Next(33,60)%59+32);var s="";
while(s.Length<28)s+=c();var a="METHINKS IT IS LIKE A WEASEL";int b=0;
while (s!=a){int m=-1;var f=s;for(int i=0;i<100;i++){
var l=string.Join("",s.Select(j=>(r.Next(20)!=0?j:c()).ToString()));
int o=Enumerable.Range(0,28).Sum(j=>l[j]==a[j]?1:0);if(o>m){f=l;m=o;}}
Console.WriteLine(b+++": "+(s=f)+" -- score: "+m);}}}}
\$\endgroup\$
3
  • \$\begingroup\$ This is broken. You need using System; or manually qualifying System. \$\endgroup\$ Commented Jan 9, 2014 at 16:34
  • \$\begingroup\$ No, you don't. ideone.com/4alNSi. \$\endgroup\$
    – tia
    Commented Jan 9, 2014 at 17:23
  • \$\begingroup\$ Ah nice. Looked at the NS declaration as a using sorry. \$\endgroup\$ Commented Jan 11, 2014 at 23:21
3
\$\begingroup\$

Lua 5.1 (502)

The minimised version:

s,t,b,c,i,q,a,d,f="ABCDFGHJYUEGKSHNCOLPQIEJUSNC","METHINKS IT IS LIKE A WEASEL",1,math.random,table.insert,1,string.sub,100,28 while q~=f do r,p={},{} for x=1,d do i(r,s) i(p,0) e="" for o=1,f do if c(1,20)==1 then if c(1,27)==1 then e=e.." " else e=e..string.char(c(65,90)) end else e=e..a(r[x],o,o) end end r[x]=e for y=1,f do if a(r[x],y,y)==a(t,y,y) then p[x]=p[x]+1 end end if p[x]==f then s=r[x] end end for x=1,d do if p[x]>=q then s,q=r[x],p[x] end end print(b..":",s,"-- score: "..q) b=b+1 end

and the easier to read version (with comments!):

s,t,b,c,i,q,a,d,f="ABCDFGHJYUEGKSHNCOLPQIEJUSNC","METHINKS IT IS LIKE A WEASEL",1,math.random,table.insert,1,string.sub,100,28
--s=random string, t=target, b=counter, c=reference to math.random, i=reference to table.insert, q=top score,a=reference to string.sub, d=constant (100), f=constant (28)
while q~=f do
    r,p={},{}
    for x=1,d do  --add 100 copies to the table of strings
        i(r,s)
        i(p,0)
        e=""
        for o=1,f do  --for each character in string
            if c(1,20)==1 then  -- 5% chance
                if c(1,27)==1 then e=e.." " else e=e..string.char(c(65,90)) end  --set it to an ASCII char between 65 and 90 (A-Z) or a space character
            else
                e=e..a(r[x],o,o)
            end
        end
        r[x]=e  --current string = mutations
        for y=1,f do
            if a(r[x],y,y)==a(t,y,y) then p[x]=p[x]+1 end
        end  --for each char increment score if it is correct
        if p[x]==f then
            s=r[x]
        end  --if 28 then final string is this!
    end
    for x=1,d do
        if p[x]>=q then s,q=r[x],p[x] end  --if this is the highest score so far, then make the string equal to this
    end
    print(b..":",s,"-- score: "..q)  --print it!
    b=b+1  --add one to the counter!
end

To be honest even though this definitely won't win, I was just glad to find and minimise a reasonably short solution for this problem! (emphasis on reasonably) :p

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

SAS - 374

%macro r;ranuni(7)%mend;%macro s;r=int(%r*27);substr(x,t,1)=byte(ifn(r,64+r,32));%mend;%macro y;char(y,t)=char(x,t)%mend;options nonotes nosource;data x;length x$28;do t=1to 28;%s;end;y="METHINKS IT IS LIKE A WEASEL";do z=1by 1;o=x;do i=1to 100;c=0;x=o;do t=1to 28;if %r<=.05then do;%s;end;c+%y;end;if c>m then do;m=c;v=x;end;end;x=v;put z":" x"-- score:" m;if m<28;end;run;

->

1 :GUUVLNUSILSRZLRBXVVCWXX HXKC -- score:2
2 :MUUVLNUSILSRZLRBXVMCWXX HXKC -- score:3
3 :MUUVLNESILSRILRBXVMCWXX HXKC -- score:4
4 :MEUVLNESILSRIRRBXVMCWXX HXKC -- score:5
....
95 :METHINKS IT IS LIKE A XEASEL -- score:27
96 :METHINKS IT IS LIKE A XEASEL -- score:27
97 :METHINKS IT IS LIKE A XEASEL -- score:27
98 :METHINKS IT IS LIKE A WEASEL -- score:28

With linebreaks/indent/comments:

%macro r;
 ranuni(7)   /* seed 0 will make new each time (seed=clock), otherwise fixed results */
%mend;
%macro s;  /* does the rand char, used both to initialize and replace; */
 r=int(%r*27); 
 substr(x,t,1)=byte(ifn(r,64+r,32)); *r=0 becomes space otherwise upper char;
%mend;
%macro y;  /*compares using new to 9.2 CHAR function which is equivalent to substr(str,start,1) */
 char(y,t)=char(x,t)
%mend;
options nonotes nosource; /*cheapest way to get clean log */
data x;
 length x$28; /*annoyingly necessary*/
 do t=1to 28;%s;end; /*initialize string*/
 y="METHINKS IT IS LIKE A WEASEL"; /*compare string */
 do z=1by 1; /*start iterating */
  o=x; /*save this iteration's string */
  do i=1to 100;
   c=0; /*score for this iteration*/
   x=o; /*string to fudge about start out clean, reusing x so no params to macro*/
   do t=1to 28;
    if %r<=.05then do;%s;end; /*if 5% then change the char out */
    c+%y; /*score that character*/
   end;
   if c>m then do; /*if on better scoring line, save it */
    m=c;
    v=x;
   end;
  end;
  x=v; *for next iter - this is cheaper than other options involving o=v due to first iter;
  put z":" x"-- score:" m;
  if m<28; *quit at 28;
 end;
run;
\$\endgroup\$
3
\$\begingroup\$

C 361 331

Not as good as Art's solution, but here's my (newbie) attempt at a C solution. 361 characters if you remove newlines and tabs.

char*w="METHINKS IT IS LIKE A WEASEL";char b[101][29];t,s,n,i,j,x,a;main(){for(;i<28;i++)b[0][i]=w[rand()%28];while(s<28){for(j=1;j<101;j++){x=0;for(i=0;i<28;i++){if(!(rand()%20))b[j][i]=w[rand()%28];else b[j][i]=b[0][i];if(b[j][i]==w[i])x++;}if(x>s){s=x;t=j;}}printf("%d: %s -- score %d\n",n++,b[t],s);for(;i>=0;--i){a=b[0][i];b[0][i]=b[t][i];b[t][i]=a;}t=0;}}

Edit: Got rid of the nested loop and used a 1D array. Was hoping it would make a bigger difference, but it only saved me 30 characters. Here's the code:

char*w="METHINKS IT IS LIKE A WEASEL";char b[2929];t,s,n,i,x;main(){for(;i<28;i++)b[i]=w[rand()%28];while(s<28){for(;i<2929;i++){if((i+1)%29){if(!(i%29))x=0;b[i]=rand()%20?b[i%29]:w[rand()%28]; x+=b[i]==w[i%29];if(x>s){s=x;t=i/29;}}}for(i=0;i<29;i++){x=b[i+t*29];b[i+t*29]=b[i];b[i]=x;}printf("%d: %s -- score %d\n",n++,b,s);t=0;}}

Edit: This is the original, ungolfed code, for those who are interested in knowing how the "golfing" was done. The code produces no warnings when compiled with GCC with -Wall and C99 enabled. Maybe you're a golfing newbie like me, or a C newbie like me, or maybe you're just curious. :) https://gist.github.com/cpx/97edbce4db3cb30c306a

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

Scala, 347 341 337 chars:

import util.Random.{nextInt=>r}
val t="METHINKS IT IS LIKE A WEASEL"
def c="ABCDEFGHIJKLMNOPQRSTUVWXYZ "(r(27))
def s(a:String)=t.zip(a).map{x=>if(x._1==x._2) 1 else 0}.sum
def w(a:String,i:Int=0){println(f"$i%2d: $a -- score: ${s(a)}")
if(s(a)!=28){w((0 to 99).map{_=>a.map(o=>if(r(20)<1) c else o)}.sortBy(s).last,i+1)}}
w(t.map(_=>c))

=>

 0: PGSHWAEPALQFTCORUKANPNUTRVXH -- score: 2
 1: PGSHWAEPALQ TCOQUKANPNUTRVXH -- score: 3
...
47: METHINKS WT IS LIKE A WEASEL -- score: 27
48: METHINKS IT IS LIKE A WEASEL -- score: 28
\$\endgroup\$
8
  • \$\begingroup\$ oops. i misread the algorithm and instead of "a 5% chance per character of that character being replaced with a random character", i had permuted a single random character. will fix. \$\endgroup\$ Commented Jan 6, 2014 at 4:39
  • \$\begingroup\$ fixed and trimmed a little! \$\endgroup\$ Commented Jan 6, 2014 at 5:36
  • \$\begingroup\$ in scala 2.10, println("%2d: %s -- score: %d".format(i,a,s(a)) can change to println(f"$i%2d: $a%s -- score: ${s(a)}%d"), saving 4 chars! \$\endgroup\$ Commented Jan 8, 2014 at 9:47
  • \$\begingroup\$ ((('A'to'Z')toSeq):+' ') == "ABCDEFGHIJKLMNOPQRSTUVWXYZ ", and the 2 %s are not necessary for printing string, saving 9 chars \$\endgroup\$
    – Chris
    Commented Jan 10, 2014 at 11:24
  • \$\begingroup\$ @Chris what version of scala did you run that under? def c=(' '+:('A'to'Z'))(r(27)) gives me error: type mismatch; found : Int required: scala.collection.generic.CanBuildFrom[scala.collection.immutable.IndexedSeq[Char],Char,?] \$\endgroup\$ Commented Jan 10, 2014 at 17:30
2
\$\begingroup\$

PHP 442

<? function r(){$n=rand(65,91);if($n==91) return ' ';else return chr($n);}function s($s){$c=0;$t='METHINKS IT IS LIKE A WEASEL';for($i=0;$i<28;$i++) if($s[$i]==$t[$i]) $c++;return $c;}function m($s){for($i=0;$i<28;$i++) if(rand(0,99)<5) $s[$i]=r();return $s;}$s='';for($i=0;$i<28;$i++) $s.=r();for($i=0;;$i++){$l=s($s);printf("%2d: %s -- score: %d\n",$i,$s,$l);if($l==28) break;$x=$s;for($j=0;$j<100;$j++){$t=m($s);if(s($t)>$l) $x=$t;}$s=$x;}

Readbly:

<?
//random char
function r(){
    $n=rand(65,91);
    if($n==91) return ' ';
    else return chr($n);
}
//score
function s($s){
    $c=0;
    $t='METHINKS IT IS LIKE A WEASEL';
    for($i=0;$i<28;$i++)
        if($s[$i]==$t[$i]) $c++;
    return $c;
}
//mutate
function m($s){
    for($i=0;$i<28;$i++)
    if(rand(0,99)<5) $s[$i]=r();
    return $s;
}
$s='';
for($i=0;$i<28;$i++) $s.=r();
for($i=0;;$i++){
    $l=s($s);
    printf("%2d: %s -- score: %d\n",$i,$s,$l);
    if($l==28) break;
    $x=$s;
    for($j=0;$j<100;$j++){
        $t=m($s);
        if(s($t)>$l) $x=$t;
    }
    $s=$x;
}
\$\endgroup\$
3
  • \$\begingroup\$ removing extra whitespace after if\for, it is at 436. you could also check $n>90 for another char \$\endgroup\$
    – Einacio
    Commented Jan 7, 2014 at 18:41
  • \$\begingroup\$ I like this, it's actually readable. I've found a few possible improvements to your r() and s() functions. Here's the changes with comments: ideone.com/4ecZQc \$\endgroup\$
    – Mr. Llama
    Commented Jan 9, 2014 at 23:12
  • \$\begingroup\$ Oh, and your printf statement can be shortened. The %s is always the same length and the %d is left justified, so you can use the following instead: printf("%2d: $s -- score: $l\n",$i); \$\endgroup\$
    – Mr. Llama
    Commented Jan 9, 2014 at 23:14
2
\$\begingroup\$

Java (632)

class C {public static void main(String[] a){String b="AAAAAAAAAAAAAAAAAAAAAAAAAAAA";for(int i=1;;i++){String c=w(b);int s=s(c);if(s==28)break;if(s(b)<s){b=c;System.out.println(i+": "+c+" -- score: "+s);}}}public static String w(String b) {StringBuffer c = new StringBuffer(b);int max = 0;for (int i=0;i<100;i++){for(int j=0;j<28;j++)if(Math.random()<.06){double d=Math.random();c.setCharAt(j,(char)(d==1?32:d*26+65));}String d=c.toString();int s=s(d);if(s>max){max=s;b=d;}}return b;}public static int s(String s){String b="METHINKS IT IS LIKE A WEASEL";int sum=0;for(int j=0;j<28;j++)sum+=s.charAt(j)==b.charAt(j)?1:0;return sum;}}

Java is such a verbose language.. :(

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

Golfscript (168 167)

:x;0['METHINKS IT IS LIKE A WEASEL'{}/]:o{;{27rand.!96*+64^}:r~}%{o=!}{100,{;.{20rand{}{;r}if}%}%{{[o\]zip{~=},,}:s~}$)@;\;x 2$+': '+1$+' -- score: '+1$s+n+:x;\)\}/;;x

Sadly, I can't seem to compress it quite to APL's level, but it's afaict currently a shared second place.

This is what it does;

# Grab the empty parameter string as initially empty output string.
:x;
# Start count at row #0
0
# Store the target string as an array in 'o'
['METHINKS IT IS LIKE A WEASEL'{}/]:o
# Create a random starting string, and define 'r' as a function generating a random char.
{;{27rand.!96*+64^}:r~}%
# Unfold as long as the latest string is different from the target string.
{o=!}
{
  // Generate 100 strings, sort by score and keep the highest.
  100,{;.{20rand{}{;r}if}%}%{{[o\]zip{~=},,}:s~}$)@;\;
  // Append the row to the output string, and increase the row number    
  x 2$+': '+1$+' -- score: '+1$s+n+:x;\)\
}/
// Clean some junk on the stack
;; 
// Output string
x
\$\endgroup\$
2
\$\begingroup\$

Python (330 321)

def b(i,s):print i,':',''.join(s),'-- score:',p(s)
from random import*;a=" ABCDEFGHIJKLMNOPQRSTUVWXYZ";i,s,t=0,choice(a)*28,"METHINKS IT IS LIKE A WEASEL";p=lambda n:sum(n[c]==t[c]for c in range(28))
while p(s)<28:b(i,s);s=sorted([[(c,choice(a))[random()<.05]for c in s]for k in[1]*100],key=lambda n:p(n))[-1];i+=1
b(i,s)

Readable version:

def b(i,s):
    print i,':',''.join(s),'-- score:',p(s)

import random as r
a=" ABCDEFGHIJKLMNOPQRSTUVWXYZ"
i,s,t=0,r.choice(a)*28,"METHINKS IT IS LIKE A WEASEL"
p=lambda n:sum(1for c in range(28)if n[c]==t[c])
while p(s)<28:
    b(i,s)
    s=sorted([[(c,r.choice(a))[r.random()<.05]for c in s]for k in[1]*100],key=lambda n:p(n))[-1];i+=1
b(i,s)

Example output:

0 : SSSSSSSSSSSSSSSSSSSSSSSSSSSS -- score: 3
1 : SSSQSSSSSSSSSSSSISSSSSSSSSSS -- score: 4
2 : SSSQISSSSSSSSSSSISSSSSSSSSSS -- score: 5
3 : SSSQISSSSSSSSSSSIKSSSSSSSSSS -- score: 6
4 : SMSQISSSSSSSISSSIKSSSSGSSSSS -- score: 7
...
53 : METHINKS IT IS UIKE A WEASEL -- score: 27
54 : METHINKS IT IS UIKE A WEASEL -- score: 27
55 : METHINKS IT IS LIKE A WEASEL -- score: 28

edit: removed a few characters based on AMKs and Timtechs answer

\$\endgroup\$
10
  • 2
    \$\begingroup\$ sum(1for c in range(28)if n[c]==t[c]) may be shortened to sum(n[c]==t[c] for c in range(28)) (-3 chars) \$\endgroup\$
    – AMK
    Commented Jan 6, 2014 at 22:21
  • 1
    \$\begingroup\$ Save 5 chars, change import random as r to from random import* and then remove the three instances of r. \$\endgroup\$
    – Timtech
    Commented Jan 7, 2014 at 15:24
  • 1
    \$\begingroup\$ Sorry, I don't speak Python. Is line zero in your sample output just a freak coincidence or does your script always start with all S's? The challenge requires starting with a string of random characters. \$\endgroup\$
    – Iszi
    Commented Jan 7, 2014 at 20:21
  • \$\begingroup\$ It starts with 28 random character, but they are always the same. \$\endgroup\$
    – PsHegger
    Commented Jan 7, 2014 at 22:24
  • \$\begingroup\$ @Iszi Lol, it never said that each individual character had to be random! PsHegger: lol starting with all S's is fitting for the name of your language ;) \$\endgroup\$
    – Doorknob
    Commented Jan 9, 2014 at 0:47
2
\$\begingroup\$

PHP (381 397 323 319 312):

<? function s(&$s,&$i=0){$t='METHINKS IT IS LIKE A WEASEL';$i=0;$c=$s;$f=28;while($f--){$n=rand(0,26);$i+=($s[$f]=($c=='_'||!rand(0,19)?chr($n?$n+64:32):$s[$f]))==$t[$f];}}$s='_';s($s);$x=$y=0;do{$f=100;while($f--){$m=$s;s($m,$i);if($i>$y){$y=$i;$l=$m;}}printf("%2d: %s -- score: $y\n",$x++,$s=$l);}while($y<28);

Readable version:

<?
function s(&$s, &$i = 0) {
    $t = 'METHINKS IT IS LIKE A WEASEL';
    $i = 0;
    $c = $s;
    $f = 28; while ($f--) {
        $n = rand(0, 26);
        $i += ($s[$f] = ($c == '_' || !rand(0, 19) ? chr($n ? $n + 64 : 32) : $s[$f])) == $t[$f];
    }
}

$s = '_';
s($s);
$x = $y = 0;

do {
    $f = 100; while ($f--) {
        $m = $s;
        s($m, $i);

        if ($i > $y) {
            $y = $i;
            $l = $m;
        }
    }

    printf("%2d: %s -- score: $y\n", $x++, $s = $l);
} while ($y < 28);

Optimization credits (319):

Optimization credits (312):

  • @Einacio's comments
\$\endgroup\$
3
  • \$\begingroup\$ i like the joint generator + randomizer \$\endgroup\$
    – Einacio
    Commented Jan 10, 2014 at 15:07
  • \$\begingroup\$ you could change both for for $f=N;while($f--){ for 3 char each. and for another char: $n=rand(0,26);[...]chr($n?$n+64:32) \$\endgroup\$
    – Einacio
    Commented Jan 10, 2014 at 15:27
  • \$\begingroup\$ Generator + randomizer + score computation. :) Thanks, I applied your optimizations. \$\endgroup\$ Commented Jan 10, 2014 at 16:40
2
\$\begingroup\$

Ruby, 218

g,s,p,t=-1,'',1;while t!=28;t,b=-1;100.times{|i|m,n='',0
28.times{|j|n+=1if(m[j]=(rand<p ?[*?A..?Z,' '].sample: s[j]))=="METHINKS IT IS LIKE A WEASEL"[j]}
b,t=m,n if n>t};puts"#{g+=1}: #{s=b} -- score: #{t}";p=0.05;end

example run

0: LRAZZMKL IKUOGEHLKPWEVNEAZWX -- score: 6
1: LRAZZMKL IKUIGEALKMWEVNEAZWX -- score: 7
2: HRAGZMKL IKUIGEALKMWEVNEAZWX -- score: 7
3: HVAGZMKL IKUISAALKYWEVNEAZWX -- score: 8
                  ...
48: METHIUKS IT IS LIKEIA WEASEL -- score: 26
49: METHINKS IT IS LIKEIA WEASEL -- score: 27
50: METHINKS IT IS LIKEIA WEASEL -- score: 27
51: METHINKS IT IS LIKE A WEASEL -- score: 28
\$\endgroup\$
2
\$\begingroup\$

Ruby - 225 202 203 198 chars

Ruby seems under-represented in this challenge so far so I thought I would give it a try! Improvements welcome.

g=-1
s=[]
puts"#{g+=1}: #{$.,s=(0..99).map{n=(r=0..27).map{|i|x=[' ',*?A..?Z].sample;rand<0.05?x:s[i]||=x};[r.count{|i|n[i]=='METHINKS IT IS LIKE A WEASEL'[i]},n*'']}.max;s} -- score: #$."until$.>27
\$\endgroup\$
4
  • \$\begingroup\$ In your output, "generation #" begins at 1 but the question specifies 0. If you init with g=-1 then it's fine. There may be a smarter way but I did it that way. Cheers, fellow RubyGolfer. \$\endgroup\$ Commented Jan 12, 2014 at 1:48
  • \$\begingroup\$ @DarrenStone Good call, thanks! Costs a character but I couldn't think of a better way. \$\endgroup\$ Commented Jan 12, 2014 at 7:10
  • 1
    \$\begingroup\$ By moving the code into the string, you can get to 198 chars: (same first two lines, then this for the rest) puts"#{g+=1}: #{$.,s=(0..99).map{n=(r=0..27).map{|i|x=[' ',*?A..?Z].sample;rand<0.05?x:s[i]||=x};[r.count{|i|n[i]=='METHINKS IT IS LIKE A WEASEL'[i]},n*'']}.max;s} -- score: #$."until$.>27 \$\endgroup\$
    – Justin
    Commented Aug 24, 2015 at 17:14
  • \$\begingroup\$ Good call! I'll edit it in. \$\endgroup\$ Commented Aug 24, 2015 at 22:08
2
\$\begingroup\$

Ruby, 206 200 199

q,i,*R=*-2..27
puts"#{i+=1}: #{$.,s=(-2..q).map{x=R.map{|j|!s||rand<0.05?[*?A..?Z,' '].sample: s[j]};[R.count{|i|x[i]=='METHINKS IT IS LIKE A WEASEL'[i]},x]}.max;q=97;s.join} -- score: #$."until$.>27

The first line is simply a fancy way to define q=-2, i=-1, and R=(0..27).to_a. All the work is done in the 2nd line:

puts"..."until$.>27 # Prints the string in quotes until we reach the desired score
     ^
     |
 +---+
 |
"#{i+=1}: #{...} -- score: #$."
   ^        ^               ^  
   +--------|---------------|-- Generation counter
 +----------+---------------|-- Current string
 |                          +-- Score of current string (interpolates the `$.` variable)
 |   
 #{$.,s=(-2..q).map{...}.max;q=97;s.join} # Generate the score & string
   ^         ^  ^   ^    ^   ^    ^
   +---------|--|---|----|---|----|------ Store the score; this variable makes
             |  |   |    |   |    |       string interpolation shorter.
             +--|---|----|---+----|------ `q` automatically takes care of generating
                |   |    |        |        the string vs randomizing the string.
                +---|----|--------|------  Make 100 (or 1 the first time) strings,
                    |    |        |        and compute their score.
                    |    +--------|------- Take the string with the max score.
 +------------------+             +------- `s` is stored as an array
 |
 x=R.map{...};[R.count{...},x] # Compute string and its score, store in array
   ^     ^    ^^       ^
   +-----|----|+-------|------ `R` is 0..27, we use the constant to save chars.
         |    +--------|------ `max` orders by first element, then second. We clearly want
         |             |       the highest score, so make the score first.
 +-------+-------------|------ Generate the string, store in `x`.
 |                     +------ Count the number of chars that overlap with 'METHINKS...'
 |                     |
 |                    {|i|x[i]=='METHINKS IT IS LIKE A WEASEL'[i]}
{|j|!s||rand<0.05?[*?A..?Z,' '].sample: s[j]}
    ^   ^         ^             ^       ^
    +---+---------|-------------|-------|---- 5% chance of randomizing, or 100% for
                  |             |       |     first string.
                  +-------------+-------|---- Sample from alphabet + ' '.
                                        +---- Don't alter the string 95% of the time
\$\endgroup\$
1
  • \$\begingroup\$ @ZachGates Glad you like the commenting style \$\endgroup\$
    – Justin
    Commented Sep 20, 2015 at 7:14
2
+100
\$\begingroup\$

Japt v2.0a0, 112 108 bytes

ª(T=Si26õdI q¹ö28
_¬í¥`Ú0ˆks Š ‰ ¦ke a Øâel`u q)x
(OpW+`: {U} -- sÖ: `+(K=[U]xV¹WÄ
K<28©ßLÆ®p513 iT ö}ÃñV o

Try it online!

-4 bytes thanks to @ETHproductions.

Unpacked & How it works

U||(T=Si26õdI q) ö28  Initialize primary input
U||                   If U is not initialized...
        26õdI           Generate uppercase alphabets
              q         Convert to string
      Si                Add space
   (T=         )        Assign to variable T
                 ö28    Sample 28 random chars from T and form a string
                        Implicitly assign to U

_q í==`Ú0ˆks Š ‰ ¦ke a Øâel`u q)x  Match counting function
_                                  Declare a function...
 q í==                         )     Convert to array of chars and pair with the next,
                                     and map with equality...
      `Ú0ˆks Š ‰ ¦ke a Øâel`u q        "methinks it is like a weasel" to uppercase
                                        split into chars
                                x    Sum (true == 1, false == 0)
                                   Implicitly assign to V

(OpW+`: {U} -- sÖ: `+(K=[U]xV) W+1  Output and increment counter
(Op                           )      Output with newline...
   W+`: {U} -- sÖ: `+                 `{W}: {U} -- score: `
                         [U]xV         Call V on [U] and force cast to number
                      (K=     )        Assign to K
                                W+1  Add 1 to W and implicitly assign to W

K<28&&ßLo@Um_p513 iT ö}} ñV o  Termination check and recursion
K<28&&                         If the match count is less than 28...
      ß                          Recurse the program with...
          Um_                      Map over chars of U...
             p513 iT                 The char repeated 513 times plus T
                     ö}              Sample a char from it
       Lo@             }           Generate array of 100 of the above
                         ñV o      Sort by V, pop the largest, pass it as U
\$\endgroup\$
1
  • \$\begingroup\$ 106 bytes, with a switch to v1.4.5. \$\endgroup\$
    – Shaggy
    Commented May 31, 2018 at 16:53
2
\$\begingroup\$

Perl 5, 219 bytes

$_="METHINKS IT IS LIKE A WEASEL";sub r{(A..Z,$")[rand 27]};sub t{~~grep/$t[$-++%28]/,pop=~/./g}$}.=r for@t=/./g;printf"%3d: %s -- score: %d
",$i++,(($})=sort{t($b)<=>t$a}map s/./rand>.05?$&:r/ger,($})x100),t$}until/$}/

Try it online!

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

J, 222 207 bytes

0(Q=:(>:@[;([:(0{]\:G=:1#.T="1])100((i.+]*1=r@20)@28{],r@27)"{@>@$<)@][":@G echo@,~' -- score: ',~":@[,': ',A{~])&>/)@(Q^:(1-(T=:(A=:u:32,65+i.26)i.'METHINKS IT IS LIKE A WEASEL')-:1>@{])^:_)@;(r=:28?@$])@27

Try it online!

Reusable function which takes no input. By default, the RNG is seeded to 7^5 (16807) in a fresh instance, so re-running the TIO link does not change the output. To demonstrate it is random, I run it twice in the link.

As I've began doing in other answers, I've included some minor golfed variations to give insights into my golfing process:

Q =: [:(0{]\:G=:1#.T="1])[:((i.@28+28*1=r@20){],r@27)"{100&$&.<
Q =: [:(0{]\:G=:1#.T="1])[:((i.@28+28*1=r@20){],r@27)"{100>@$<
Q =: [:(0{]\:G=:1#.T="1])100((i.@28+28*1=r@20){],r@27)"{@>@$<
Q =: [:(0{]\:G=:1#.T="1])100((i.+]*1=r@20)@28{],r@27)"{@>@$<

(Somewhat) ungolfed

Try it online!

A=:u:32,65+i.26
T=:A i.'METHINKS IT IS LIKE A WEASEL'

r=:28?@$]
grade=:1 #. T ="1 ]
mut =: (i.@28+28*1=r@20){],r@27
gen =: [: mut"{ 100&$&.<
best =: 0 { ] \: grade
Q =: best@gen

Qcat =: ] [ [: echo (":@grade) ,~ ' -- score: ',~":@[,': ',A{~]
Qit =: (>:@[ ; Q@Qcat)&>/
Qcontinue =: 1&([-T-:>@{)
f =: 0 Qit@(Qit^:Qcontinue^:_)@;r@27
f ''

Old solution, 222 bytes

0 I@((I=:(>:@[;([:(0{]\:G"1)((28|i.100 28)+28*1=r@20){"1],"1 r@27)@(][[:echo":@(G=:1#.T=]),~' -- score: ',~":@[,': ',A{~]))&>/)^:(1&([-(T=:(A=:u:32,65+i.26)i.'METHINKS IT IS LIKE A WEASEL')-:>@{))^:_)@;0{(r=:100 28?@$])@27

Try it online!

I think there are still bytes to be saved: Right now, the program makes all the random mutations at once, and I think writing the mutation logic to be on a single sentence, and running that individually might be shorter. But I must work on this later. Sure enough, it was shorter!

(Somewhat) Ungolfed

A=:u:32,65+i.26
T=:A i.'METHINKS IT IS LIKE A WEASEL'
r=:100 28?@$]

grade =: 1 #. T = ]
Qgen =: ((28|i.100 28)+28*1=r@20){"1],"1 r@27
Qsel =: (0{]\:grade"1)
Q =: [: Qsel Qgen

Qcat =: ] [ [: echo (":@grade) ,~ ' -- score: ',~":@[,': ',A{~]
Qit =: (>:@[ ; Q@Qcat)&>/
Qcontinue =: 1&([-T-:>@{)

f =: 0 Qit@(Qit^:Qcontinue^:_)@;0{r@27
f ''
\$\endgroup\$
2
\$\begingroup\$

Zsh +coreutils, 260 235 215 209 201 bytes

p()jot -cr $1 @ Z;T=METHINKS@IT@IS@LIKE@A@WEASEL;S=(`p 28`)
for i ({0..99}){V= j=;printf $i:\ ${(j::)S//@/ }
for x ($S)((#x!=##$T[++j]&&RANDOM%20))&&S[j]=`p 1`||((V++))
<<<" -- score: $V";((V==28))&&?}

Try it online! 209b 215b 235b 260b

Found the question a bit confusing, but eventually figured it out. For a minute I thought I had disproved* evolution!! (*actually a limitation of the zsh $RANDOM function in subshells)

Notes:
* Using @ in the target string allows me to use ASCII range 64-90
* Using jot -c -r 28 64 90 is better than repeatedly doing ${(#)$((RANDOM%27+64))}
* -20 bytes: removed "", squished jot parameters to -cr 1 @ Z, replaced exit with ? (crashes)
* -6 bytes: changed $S from string to array
* -8 bytes: replaced $n with expression ##$T[++j]; put jot calls into function p()

\$\endgroup\$
3
  • 1
    \$\begingroup\$ Nice solution (+1), good idea to use @..Z, but I disagree that the question is poorly defined - it seems pretty obvious to me from reading the task description and looking at the example what the intended program should do \$\endgroup\$ Commented Aug 31 at 13:07
  • \$\begingroup\$ Step 2 was confusing; I don't think we are supposed to create 100 mutated copies at each iteration?? \$\endgroup\$
    – roblogic
    Commented Aug 31 at 15:47
  • 1
    \$\begingroup\$ Make 100 copies of the string. In each copy, each character has a 5% chance of being replaced. \$\endgroup\$ Commented Aug 31 at 19:29
1
\$\begingroup\$

Ruby - 410

#!/usr/bin/env ruby
C,RC=[32]+(65..90).to_a,->{C[rand(27)].chr}
T,CO,CH,OU,s,sc,a,aa,z,TR="METHINKS IT IS LIKE A WEASEL",->x{a=0;(0...28).each{|z|a+=1 if x[z]==T[z]};a},->{a[aa.rindex(sc)]},->x,y{print x;print " Score: ";puts y},(0...28).map{RC[]}.join,0,[0],[0],0,->{rand(20)==0}
until sc==28
a=[s]*100;aa=[0]*100;(0...100).each{|x|(0...28).each{|y|a[x][y]=RC[] if TR[]};z=CO[a[x]];aa[x]=CO[a[x]];OU[a[x],z]};sc=aa.max;s=CH[] end

Edit* It's currently failing (for some reason a[any] is being set to 0 (type=>fixnum)). However, the actual design is right, I just need to find the bug causing this to happen (it's very mysterious)

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

Python 284

from random import*
C=choice
A=map(chr,range(65,91)+[32])
s=[C(A)for i in[0]*28]
N=x=0
while N!=28:N,s=max((len([i for i,j in zip(X,"METHINKS IT IS LIKE A WEASEL")if i==j]),X)for X in[[c if random()<.95 else C(A)for c in s]for i in[0]*100]);print`x`+":",''.join(s),"-- score:",N;x+=1
\$\endgroup\$
1
\$\begingroup\$

GolfScript 207

{91,{64>},32+''+27rand=}:g;'METHINKS IT IS LIKE A WEASEL':s;{`{\.@\=s@==}+s,,\%{+}*}:c;0:k;{k):k\.': '@@c' -- score: '\++++n+}:f;[{g}]28*{~}%{s=!}{{`100,\{\;{100rand 5>{ }{;g}if}%''+}+}~%{c}$)\;{ }%}/{f}/s f

Below is a slightly unpacked version of the above GolfScript with explanation. Since I don't think it could beat the APL or some other answers, I didn't bother to truly minify it. I think that with inlining variable declarations, eliminating unnecessary variables, and other such tricks, this code could achieve approximately 190 characters without really changing the algorithm. I think about about 10-15 characters could be removed if I better sorted out the conversion between arrays of ASCII values and strings.

#g is a function that returns the ASCII value of a random character or space. 
#The ASCII values for capital letters are 65-90, and 32 is space.
{91,{64>},32+''+27rand=}:g; 

#s is the string of interest.
'METHINKS IT IS LIKE A WEASEL':s;

#c is a function that returns the 'score' of a given string
#(the number of correct characters in the correct place).
{`{\.@\=s@==}+s,,\%{+}*}:c;

#t is a function that transforms a given string according to 
#the specification (by replacing characters with a random character 5% of the times).
{{100rand 5>{ }{;g}if}%''+}:t;

#i is the initial random string.
[{g}]28*{~}%:i;

#Use '/' to unfold the initial value until the string is equal to the string of interest.
#Every loop, do the transformation 100 times, then sort by score c, and then take the top scoring string.
#Aggregate the results into an array a.
i{s=!}{{`100,\{\;t}+}~%{c}$)\;{ }%}/:a;

#Instantiate a counter variable k
0:k;

#f is the formatting function, that takes a string and formats it according to the spec.
{k):k\.': '@@c' -- score: '\++++n+}:f;

#Format every string in the array, and then format the string of interest
a{f}/
s f
\$\endgroup\$
1
\$\begingroup\$

JavaScript - 312

There is already a shorter JS solution above but it is using experimental pointer functions, so I thought I'd throw in another solution that is running in any JS environment:

for(r=Math.random,R=function(){return'METHINKS CODZAWFLBUGYQRXVJP'[~~(r()*27)]},_=[],_.$=n=0,D=function(s){for(c=[],c.$=i=0;i<28;){c[i]=s&&r()<.95?s[i]:R();_=(c.$+=c[i]=='METHINKS IT IS LIKE A WEASEL'[i++])>_.$?c:_};return c},D();_.$<28;){for(j=0;j++<1e2;)D(_);console.log(n+++': '+_.join('')+' -- score: '+_.$)}
\$\endgroup\$

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.