原件存檔 Original files吳哲宇 Che-Yu Wu

國三下(2010 年 1 月到 4 月)Grade 9, second term (January to April 2010)

演化:把 ff 改成 fun 的字串生物Evolution: string-command creatures that turn ff into fun

演化的網頁版,照 2010 年 4 月 16 日的原始碼改寫。打開時每秒走 60 步,可以調快。Evolution running in the browser, rewritten from the source of 16 April 2010. It opens at 60 steps a second and can be sped up.

在新分頁開整站 Open the whole site ↗

上面跑的就是這個程式。2026 年照 Form1.vb、ClsEater.vb、ClsFood.vb 逐行改寫成 JavaScript:計時器每跳一次,網頁版走一步,先移動每一顆食物,再輪到每一隻生物找食物、吃、試 ff、繁殖、交配。VB 把小數存進整數時遇到 .5 取偶數、Collection 可以一邊跑一邊增刪、亂數的上下限,都照 VB 的規則寫,因為生物怎麼走、什麼時候死,取決於這些細節。

程式裡幾個看起來像寫錯的地方也照留。食物會躲生物,但找最近生物的那一行拿食物和自己比,結果每顆食物都在躲集合裡的最後一隻。交配時有六分之一的機率多生一隻,牠的位置是兩隻親代的座標直接相加,常常落在畫面外。食物越過右邊或下邊,程式先把它搬到最左邊,下一行又搬回最右邊,於是卡在視窗右下角外面,生物會追出去吃。狀態列裡「在畫面外」的數字就是這些食物。

照這份程式碼跑了六次,每次三百萬步。五次演化出字:yoy 5 次、cat 10 次、fly 4 次、fun 7 次,和 ff--result.txt 裡的字幾乎是同一組,frank、facebook、majer 一次也沒有。第一次成功最早在第 327,044 步,最晚在第 1,290,427 步,另一次三百萬步都沒有成功。用每秒 60 步要等一個半到六個小時,調到每秒 3,840 步大約一分半到六分鐘。

和原版不同的地方:原本的視窗大小與計時器間隔記在 Form1.Designer.vb,這次改寫沒有用到這個檔,畫面預設 960 × 600、每秒 60 步,手機上等比縮小。原版每 500 步把所有生物的基因存進 SaveText 資料夾,網頁版不存檔,Diary 和 ff--result 改成列在畫面下方。依程式碼推算,生物超過約 63 隻時原版會因為整數溢位跳出錯誤視窗,網頁版讓它繼續跑,並在下方註明。

What runs above is the program itself, rewritten line by line in JavaScript in 2026 from Form1.vb, ClsEater.vb and ClsFood.vb. Each tick of the timer is one step: first every piece of food moves, then each creature in turn looks for food, eats, tries ff, copies itself and mates. VB's rounding to even when a fraction is stored in an Integer, a Collection that can grow and shrink while it is being looped over, and the bounds of the random numbers all follow VB's rules, because how the creatures move and when they die depends on them.

A few things that look like mistakes are kept as well. Food runs from creatures, but the line meant to find the nearest one compares the food with itself, so every piece of food runs from the last creature in the collection. One time in six a mating produces an extra offspring placed at the sum of its parents' coordinates, often off screen. Food that crosses the right or bottom edge is moved to the far left and, one line later, back to the far right, so it waits just outside the bottom-right of the window and creatures chase it out there; the "outside the window" count in the status line is that food.

This code was run six times for three million steps each. Five runs evolved words: yoy 5 times, cat 10, fly 4, fun 7, much the same set as in ff--result.txt; frank, facebook and majer never appeared. The first success came at step 327,044 at the earliest and step 1,290,427 at the latest, and one run had none in three million steps. At 60 steps a second that is a wait of one and a half to six hours; at 3,840 steps a second, about one and a half to six minutes.

Where it differs from the original: the window size and timer interval are set in Form1.Designer.vb, which this rewrite did not use, so the view defaults to 960 × 600 at 60 steps a second and scales down on a phone. Every 500 steps the original saved every creature's gene into the SaveText folder; the web version saves nothing and lists the Diary and ff--result lines under the view instead. Reading the code, the original would stop with an Integer overflow error once there are more than about 63 creatures; the web version carries on and says so below the view.

畫面上是一群會追食物的小方塊和小圓。每個生物帶著一串指令,基因的樣子像 ADD s,ADH f。程式對每個新生的生物做同一件事:把字串 "ff" 丟進它的指令串,依序執行,得到的字如果在 goodword 清單裡,就在 ff--result.txt 記一筆「ff-->fun」,接著記下這個生物的基因。goodword 清單有 15 個字(frog 寫了兩次):fun、cell、hello、nice、fly、frog、yeah、yoy、flash、frank、facebook、majer、favorite、cat。

4 月版的 ClsEater 會執行六種指令:ADD 在尾巴接一個字,ADH 在頭上加一個字,LFT 把整串倒過來,LRD 與 RRD 留下左邊或右邊幾個字,DEL 刪掉第幾個字。另外三種指令 ERA(刪一段)、SUB(取一段)、SPA(插底線)來自 2009 年 11 月的文字編輯習題,放在習題程式 B 的資料夾裡,食物物件的基因變異時還會長出它們。

生物吃到食物,就用亂數在 ADD、ADH、LRD、RRD、LFT、DEL 裡挑一個,接到自己的指令串後面,吃到優等食物一次學兩條。繁殖時每條指令有一半的機率被複製給後代。成功過的生物例外:它和它往下 9 代的後代,複製時都完整保留指令串。

On screen a crowd of small squares and circles chases food. Each creature carries a list of commands, its gene looking like ADD s,ADH f. The program does the same thing for every newborn: it feeds the string "ff" through the creature's commands in order, and if the result is on the goodword list it writes "ff-->fun" (or the word it got) to ff--result.txt, followed by the creature's gene. The goodword list has 15 entries (frog appears twice): fun, cell, hello, nice, fly, frog, yeah, yoy, flash, frank, facebook, majer, favorite, cat.

The April ClsEater runs six commands: ADD appends a character, ADH puts one at the front, LFT reverses the string, LRD and RRD keep the left or right n characters, and DEL deletes the n-th character. Three more, ERA (erase a stretch), SUB (take a stretch) and SPA (insert an underscore), come from a text-editing exercise of November 2009 that sits in the folder of exercise program B, and the food objects' genes grow them when they mutate.

A creature that eats food picks one of ADD, ADH, LRD, RRD, LFT and DEL at random and appends it to its own list; superior food teaches two. When a creature reproduces, each command has a one in two chance of being copied to the offspring, The exception is a creature that has succeeded: it and its next 9 generations of descendants copy the whole list.

    Public Function WordTran(ByVal Texta, ByVal Command) As String
        Try
            CommandText = Split(Command)(0)
            Select Case CommandText
                Case "ADD"
                    Texta += Split(Command)(1)
                Case "ADH"
                    Texta = Split(Command)(1) + Texta
                Case "LFT"
                    Texta = LeftToRight(Texta)
                Case "LRD"
                    Texta = Microsoft.VisualBasic.Left(Texta, CInt(Split(Command)(1)))
                Case "RRD"
                    Texta = Microsoft.VisualBasic.Right(Texta, CInt(Split(Command)(1)))
                Case "DEL"
                    Texta = Microsoft.VisualBasic.Left(Texta, CInt(Split(Command)(1)) - 1) & Microsoft.VisualBasic.Mid(Texta, CInt(Split(Command)(1)) + 1)
                Case Else
                    If BigCommands.Item(CommandText) IsNot Nothing Then
                        For Each Commanda As String In BigCommands.Item(CommandText)
                            WordTran(Texta, Commanda)
                        Next
                    End If
            End Select
            Return Texta
        Catch ex As Exception
            Return Texta
        End Try
        Return Texta
    End Function
ClsEater.vb 的 WordTran:每個指令對字串做什麼。指令數字超出範圍時 VB 丟出錯誤,Catch 把字串原樣傳回。WordTran in ClsEater.vb: what each command does to the string. When a number is out of range VB throws an error and the Catch returns the string unchanged.
                        objEater.Live += 25
                        Select Case ran.Next(0, 6)
                            Case 0
                                objEater.Commands.Add("ADD " & Chr(97 + ran.Next(0, 26)))
                            Case 1
                                objEater.Commands.Add("ADH " & Chr(97 + ran.Next(0, 26)))
                            Case 2
                                objEater.Commands.Add("LRD " & ran.Next(0, 14))
                            Case 3
                                objEater.Commands.Add("RRD " & ran.Next(0, 14))
                            Case 4
                                objEater.Commands.Add("LFT")
                            Case 5
                                objEater.Commands.Add("DEL " & ran.Next(0, 14))
                        End Select
Form1.vb:生物吃到普通食物,隨機學一條指令。Form1.vb: a creature that eats ordinary food learns one random command.
            If objEater.TryWord = False Then
                testword = objEater.result("ff")
                Print("Try " + testword)
                objEater.ResultText = testword
                If goodword.Contains(testword) Then
                    BestObjEater = objEater
                    '(這裡省略 10 行被註解掉的舊寫法)
                    Label4.Text = "成功!!!!!!~" + oktime.ToString
                    oktime += 1
                    My.Computer.FileSystem.WriteAllText("C:\Users\Majer\Desktop\演化\演化\ff--result.txt", "ff-->" + testword + vbNewLine, True)
                    My.Computer.FileSystem.WriteAllText("C:\Users\Majer\Desktop\演化\演化\ff--result.txt", "|" + BestObjEater.DNA + "|" + vbNewLine, True)
                    objEater.TruelyNum = 10
                End If
                objEater.TryWord = True
Form1.vb:對新生的生物試 "ff",成功就寫進 ff--result.txt。Form1.vb: try "ff" on a newborn and write a success to ff--result.txt.

ff--result.txt 留下 32 筆成功紀錄:fun 13 筆、yoy 12 筆、fly 4 筆、cat 2 筆、frog 1 筆。最短的 fly 只有 16 條指令,最長的 yoy 有 4,574 條。短的那一筆裡有 LRD 0 這種把字串清空、再用 ADD 和 ADH 重新拼起來的做法。cell、hello、nice、yeah、flash、frank、facebook、majer、favorite 在這份檔案裡沒有成功紀錄。

SaveText 資料夾有 37 個實驗資料夾,其中 36 個有 Diary.txt,逐行記下每個新生物試出的字(Try fffs)和族群的增減(Copy OK Live 2、Died OK Live 5)。起始生物的基因是 ADD s,ADH f,所以這 36 份 Diary 開頭第一個 Try 都是 fffs。

ff--result.txt holds 32 success records: fun 13, yoy 12, fly 4, cat 2, frog 1. The shortest, a fly, takes 16 commands and the longest, a yoy, 4,574. The short one uses LRD 0 to empty the string and then rebuilds it with ADD and ADH. cell, hello, nice, yeah, flash, frank, facebook, majer and favorite have no success record in this file.

The SaveText folder holds 37 experiment folders, 36 of them with a Diary.txt that logs, line by line, the word each newborn produced (Try fffs) and the population going up and down (Copy OK Live 2, Died OK Live 5). The starting creature's gene is ADD s,ADH f, so the first Try in each of those 36 Diaries is fffs.

ff-->fly
|Speed 36;
 Shape AA;
 Live 3522;
 CopyTime 31;
 Width 24;
 Hash 18288157;
 R 90;
 G 61;
 B 34;
 WordsTrans-
 ADD l,ADH t,ADH n,LFT,LRD 13,DEL 13,RRD 7,LRD 2,LRD 5,ADH t,
 LFT,LRD 0,LRD 0,ADD l,ADD y,ADH f|
ff--result.txt 第 47、48 行:一筆 fly 的紀錄(為了排版,基因在逗號處換行)。Lines 47 and 48 of ff--result.txt: one record for fly (the gene is wrapped at commas for layout).
Time-->0
Try fffs
Copy OK Live 2
Try fffs
Copy OK Live 3
Try fffs
Copy OK Live 4
Try fffs
Tor OK Live 5
Try ssffff
Try fffs
Died OK Live 5
Copy OK Live 6
Try ffs
Try fffs
Tor OK Live 8
Try dfffss
Try fffs
Tor OK Live 10
Try
Died OK Live 9
Try dfffss
Died OK Live 9
Died OK Live 8
Copy OK Live 9
Try cfff
SaveText/No_0/Diary.txt 開頭 26 行。The first 26 lines of SaveText/No_0/Diary.txt.

下面是重播器。選一筆 ff--result.txt 裡的紀錄,可以一步一步看 "ff" 怎麼變成目標字,也可以切換中英文。我照 ClsEater.vb 的寫法重新實作了這些指令,再把檔案裡的成功紀錄逐筆跑過,30 筆都得到記錄上的字。選單最後面另有兩筆記錄,照記錄的基因串跑不出目標字,原因見重播器下方的說明。

Below is the replayer. Pick a record from ff--result.txt and watch "ff" turn into the target word one command at a time, in Chinese or English. I re-implemented the commands as ClsEater.vb writes them and ran the recorded successes through it: 30 of them give the recorded word. Two more records, at the end of the menu, do not reach their target when their gene is run as recorded; the note under the replayer says why.

指令串重播器。預設是 fly 那筆,16 步。The command-string replayer, opened on the fly record, 16 steps.

在新分頁開整站 Open the whole site ↗

第 1 行的 fun 用到 SUB,4 月版的 ClsEater 不認得這個指令,我改用習題程式 B 的寫法跑它,得到 fun。習題的 pb.in 有 7 條指令,照同一套寫法跑出來的 7 列,和 pb.out 逐列相同。

第 27 行(fly)和第 29 行(fun)跑不出來。記錄的是生物出生時的基因,生物在第一個畫面可能已經吃了食物、學了新指令才被檢查,新指令沒有寫回記錄。第 29 行是起始生物,基因只有 ADD s,ADH f,只能得到 fffs。這是推測,沒有直接證據。

The fun on line 1 uses SUB, which the April ClsEater does not know, so I ran it the way exercise program B writes it and got fun. The exercise's pb.in holds 7 commands, and the 7 lines produced by the same code match pb.out line for line.

Lines 27 (fly) and 29 (fun) do not reproduce. What is logged is the gene a creature was born with, and in its first frame a creature may already have eaten food and learned new commands before it is checked; those commands are not written back. Line 29 is the starting creature, whose gene is only ADD s,ADH f and gives fffs. This is a guess with no direct evidence.