pangolin กลายพันธุ์


28

นี่คือความท้าทายในการเล่นกอล์ฟซึ่งคุณต้องคิดโปรแกรมที่ทำหน้าที่เหมือนควินินหรือควินินที่ปรับเปลี่ยนตัวเองเพื่อแสดงการเรียนรู้ของเครื่อง

พื้นหลัง

มีโปรแกรมปัญญาประดิษฐ์พื้นฐานที่เรียกว่า 'เกมลิ่น' ซึ่งจะอธิบายเป็นที่นี่ แนวคิดพื้นฐานคือโปรแกรมเมื่อเรียกใช้ในครั้งแรกที่ถาม:

ตกลงได้โปรดคิดถึงบางสิ่ง

มันเป็นลิ่นหรือ?

จากนั้นคุณสามารถตอบกลับอย่างใดอย่างหนึ่ง:

ใช่

ในกรณีนี้มันบอกว่า:

ดี. นั่นเป็นเรื่องง่ายมาก

หรือถ้าไม่มันบอกว่า:

โอ้ ถ้างั้นคุณก็ชนะ - คุณคิดอะไรอยู่

ซึ่งคุณอาจพูดว่า:

สุนัข

ซึ่งมันจะบอกว่า

กรุณาให้คำถามกับฉันเกี่ยวกับสุนัขดังนั้นฉันสามารถบอกความแตกต่างระหว่างสุนัขกับ pangolin ได้

คุณอาจตอบกลับ

กินมดไหม

จากนั้นจะถาม:

คำตอบสำหรับสุนัขคืออะไร?

ที่คุณจะพูดว่า

ไม่

และมันจะบอกว่า

ขอบคุณ

ครั้งต่อไปที่มันทำงานมันจะถามคำถามข้างต้นและจะสร้างต้นไม้ไบนารีของคำถามดังกล่าว

ความท้าทาย

พอพื้นหลัง ความท้าทายนี้คือการเขียนโปรแกรม pangolin ที่ดัดแปลงด้วยตนเอง กฎมีดังนี้:

  1. เอาท์พุท Program (ตามที่อธิบายไว้ข้างต้น) STDERRควรจะ คำตอบสุดท้ายจะเป็น "ดีนั่นเป็นเรื่องง่ายมาก" หรือ "ขอบคุณ" หลังจากนี้ควรส่งออกทั้งเวอร์ชันปัจจุบันของโปรแกรมหรือเวอร์ชันใหม่ของโปรแกรมที่รวมคำถามไว้STDOUTด้วย ไม่มีคำตอบที่เขียนในภาษาที่ไม่สนับสนุนการเขียนSTDOUTและSTDERRหรือการอ่านจากSTDINจะถูกต้อง

  2. กล่าวอีกนัยหนึ่งภายใต้ UNIX คุณสามารถเรียกใช้โปรแกรมดังนี้:

ตัวอย่าง:

$ mylanguage myprogram > myprogram.1
[dialog goes here]
$ mylanguage myprogram1 > myprogram.2
[dialog goes here]
  1. โปรแกรมต้องใช้พรอมต์ที่ระบุทุกประการ (เนื่องจากการตัดทอนพรอมต์แสดงว่าไม่มีทักษะ) พรอมต์คือ (โดยไม่ใส่เครื่องหมายอัญประกาศและแทนที่ด้วย% s) ดังต่อไปนี้:

รายการ:

"OK, please think of something"
"Is it %s?"
"Good. That was soooo easy."
"Oh. Well you win then -- What were you thinking of?"
"Please give me a question about %s, so I can tell the difference between %s and %s"
"What is the answer for %s?"
"Thanks"
  1. เมื่อคาดหวังคำตอบใช่ / ไม่ใช่โปรแกรมของคุณควรยอมรับyหรือไม่ว่าyesในกรณีใดสำหรับ 'ใช่' และnหรือnoในกรณีใด ๆ สำหรับ 'ไม่' สิ่งที่คุณทำกับอินพุตที่ไม่เป็นไปตามนั้นขึ้นอยู่กับคุณ ตัวอย่างเช่นคุณอาจตัดสินใจที่จะรับคำตอบใด ๆ ที่ขึ้นต้นด้วยyหรือYเป็น 'ใช่' และสิ่งอื่นใดที่ไม่ใช่

  2. คุณอาจจะสมมติว่าชื่อของสิ่งที่ให้มาและคำถามที่ประกอบด้วยตัวอักษร ASCII, ตัวเลข, ช่องว่างยัติภังค์เครื่องหมายคำถามเครื่องหมายจุลภาคมหัพภาคทวิภาคและอัฒภาคคือพวกเขาตรงกับ ^[-?,.;: a-zA-Z]+$regex หากคุณสามารถรับมือได้มากกว่านั้น (โดยเฉพาะอย่างยิ่งตัวละครที่อ้างถึงในภาษาที่คุณเลือก) คุณจะต้องพอใจ แต่ไม่ได้รับคะแนนพิเศษใด ๆ

  3. โปรแกรมของคุณอาจจะไม่ได้อ่านหรือเขียนไฟล์ใด ๆ (ไม่รวมSTDIN, STDOUTและSTDERR) หรือจากเครือข่ายนั้น โดยเฉพาะมันไม่สามารถอ่านหรือเขียนรหัสของตัวเองจากดิสก์ สถานะของมันจะต้องถูกบันทึกไว้ในรหัสโปรแกรมเอง

  4. เมื่อโปรแกรมทำงานและคาดเดาคำตอบได้อย่างถูกต้องโปรแกรมจะต้องทำงานอย่างถูกต้องเหมือนกับ quine นั่นคือต้องเขียนไปยังSTDOUTรหัสของตัวเองไม่เปลี่ยนแปลง

  5. เมื่อโปรแกรมรันและเดาคำตอบไม่ถูกต้องจะต้องเข้ารหัสคำถามและคำตอบใหม่ที่ให้ไว้ในรหัสของตัวเองและเขียนลงSTDOUTในรหัสของมันเองดังนั้นจึงสามารถแยกความแตกต่างระหว่างการเดาดั้งเดิมกับวัตถุใหม่ที่ให้ไว้ใน นอกจากนี้เพื่อแยกความแตกต่างระหว่างวัตถุที่ได้รับก่อนหน้านี้ทั้งหมด

  6. คุณจะต้องสามารถรับมือกับการรันต่อเนื่องหลาย ๆ ครั้งของซอฟต์แวร์เพื่อเรียนรู้เกี่ยวกับวัตถุจำนวนมาก ดูที่นี่สำหรับตัวอย่างการวิ่งหลายครั้ง

  7. การทดสอบจะกระทำที่ลิงค์ในหัว (เห็นได้ชัดว่าครอบคลุมเฉพาะSTDINและSTDERRโต้ตอบ)

  8. ช่องโหว่มาตรฐานไม่รวมอยู่


โปรแกรมควรจะสามารถกลายพันธุ์ได้หลายครั้งและให้การสนับสนุนสัตว์มากกว่า 2 ตัวหรือไม่? ถ้าเป็นเช่นนั้นคุณช่วยยกตัวอย่างบทสนทนา"กรุณาให้คำถามเกี่ยวกับ ... " กับฉันเมื่อมีสัตว์สองตัวหรือมากกว่าที่โปรแกรมรู้?
Cristian Lupascu

เกิดอะไรขึ้นถ้าผู้ใช้พูดว่า "สุนัข" แทนที่จะเป็น "สุนัข"? เราจะแยกประโยคเพื่อตรวจจับ "a / an" หรือเราจะรักษาคำตอบอย่างแท้จริง? ฉันถือว่าได้รับพร้อมท์ที่คุณให้ (% s)
coredump

1
@coredump หากผู้ใช้ระบุว่า "dog" ไม่ใช่ "a dog" ดังนั้นการตอบกลับจะไม่ใช้ไวยากรณ์ นั่นไม่ใช่ปัญหา
abligh

1
OOF การพยายามทำสิ่งนี้ใน Runic จะเป็นฝันร้าย เหตุผลหลักคือการเดินสายไฟบิตทั้งหมดเพื่อจัดการกับสตริงอินพุตที่กำหนดเอง (ซึ่งจำเป็นต้องแสดงเป็นตัวอักษรสตริงในโปรแกรมเอาต์พุตผลลัพธ์) โดยทั่วไปจะเป็นไปไม่ได้ โอ้และ Runic ไม่สามารถส่งออกไปยัง STDERR ได้
Draco18s

1
มันดูเหมือนเกม "สนุก" ดังนั้นแทนที่จะเล่นกอล์ฟฉันทำcodepenที่คุณสามารถเล่นเกม Pangolin กับเนื้อหาในหัวใจของคุณ สนุก!
Skidsdev

คำตอบ:


20

เสียงกระเพื่อมสามัญ 631 576

(let((X"a pangolin"))#1=(labels((U(M &AUX(S *QUERY-IO*))(IF(STRINGP M)(IF(Y-OR-N-P"Is it ~A?"M)(PROG1 M(FORMAT S"Good. That was soooo easy.~%"))(LET*((N(PROGN(FORMAT S"Oh. Well you win then -- What were you thinking of?~%")#2=(READ-LINE S)))(Q(PROGN(FORMAT S"Please give me a question about ~A, so I can tell the difference between ~A and ~A~%"N N M)#2#)))(PROG1(IF(Y-OR-N-P"What is the answer for ~A?"N)`(,Q ,N ,M)`(,Q ,M ,N))(FORMAT S"Thanks~%"))))(DESTRUCTURING-BIND(Q Y N)M(IF(Y-OR-N-P Q)`(,Q ,(U Y),N)`(,Q ,Y,(U N)))))))(write(list'let(list`(X',(U x)))'#1#):circle t)()))

ตัวอย่างเซสชัน

ตั้งชื่อสคริปต์pango1.lispและดำเนินการดังต่อไปนี้ (โดยใช้ SBCL):

~$ sbcl --noinform --quit --load pango1.lisp > pango2.lisp
Is it a pangolin? (y or n) n
Oh. Well you win then -- What were you thinking of?
a cat
Please give me a question about a cat, so I can tell the difference between a cat and a pangolin
Does it sleep a lot?
What is the answer for a cat? (y or n) y
Thanks

อีกรอบเพิ่มหมี:

~$ sbcl --noinform --quit --load pango2.lisp > pango3.lisp
Does it sleep a lot? (y or n) y

Is it a cat? (y or n) n
Oh. Well you win then -- What were you thinking of?
a bear
Please give me a question about a bear, so I can tell the difference between a bear and a cat
Does it hibernate?
What is the answer for a bear? (y or n) y
Thanks

เพิ่มเฉื่อยชา (เราทดสอบกรณีที่คำตอบคือ "ไม่"):

~$ sbcl --noinform --quit --load pango3.lisp > pango4.lisp
Does it sleep a lot? (y or n) y

Does it hibernate? (y or n) n

Is it a cat? (y or n) n
Oh. Well you win then -- What were you thinking of?
a sloth
Please give me a question about a sloth, so I can tell the difference between a sloth and a cat
Does it move fast?
What is the answer for a sloth? (y or n) n
Thanks

ทดสอบไฟล์สุดท้าย:

~$ sbcl --noinform --quit --load pango4.lisp > pango5.lisp
Does it sleep a lot? (y or n) y

Does it hibernate? (y or n) n

Does it move fast? (y or n) y

Is it a cat? (y or n) y
Good. That was soooo easy.

หมายเหตุ

  • ฉันลืมพิมพ์ครั้งแรก"Thanks"นี่ไง
  • อย่างที่คุณเห็นคำถามตามมา(y or n)ซึ่งเป็นเพราะฉันใช้y-or-n-pฟังก์ชันที่มีอยู่ ฉันสามารถอัปเดตคำตอบเพื่อลบผลลัพธ์นี้หากจำเป็น
  • Common Lisp มี*QUERY-IO*สตรีมแบบสองทิศทางที่อุทิศให้กับการโต้ตอบกับผู้ใช้ซึ่งเป็นสิ่งที่ฉันกำลังใช้อยู่ที่นี่ เอาต์พุตมาตรฐานและการโต้ตอบกับผู้ใช้จะไม่ยุ่งเหยิงซึ่งตามมาด้วย IMHO เจตนารมณ์ของคำถาม
  • การใช้SAVE-LISP-AND-DIEจะเป็นวิธีการที่ดีกว่าในการปฏิบัติ

สร้างผลลัพธ์

นี่คือสคริปต์ที่สร้างขึ้นล่าสุด:

(LET ((X
       '("Does it sleep a lot?"
              ("Does it hibernate?" "a bear"
               ("Does it move fast?" "a cat" "a sloth"))
              "a pangolin")))
  #1=(LABELS ((U (M &AUX (S *QUERY-IO*))
                (IF (STRINGP M)
                    (IF (Y-OR-N-P "Is it ~A?" M)
                        (PROG1 M (FORMAT S "Good. That was soooo easy.~%"))
                        (LET* ((N
                                (PROGN
                                 (FORMAT S
                                         "Oh. Well you win then -- What were you thinking of?~%")
                                 #2=(READ-LINE S)))
                               (Q
                                (PROGN
                                 (FORMAT S
                                         "Please give me a question about ~A, so I can tell the difference between ~A and ~A~%" 
                                         N N M)
                                 #2#)))
                          (PROG1
                              (IF (Y-OR-N-P "What is the answer for ~A?" N)
                                  `(,Q ,N ,M)
                                  `(,Q ,M ,N))
                            (FORMAT S "Thanks~%"))))
                    (DESTRUCTURING-BIND
                        (Q Y N)
                        M
                      (IF (Y-OR-N-P Q)
                          `(,Q ,(U Y) ,N)
                          `(,Q ,Y ,(U N)))))))
       (WRITE (LIST 'LET (LIST `(X ',(U X))) '#1#) :CIRCLE T)
       NIL))

คำอธิบาย

ต้นไม้การตัดสินใจสามารถ:

  • สตริงเช่น"a pangolin"ซึ่งหมายถึงใบไม้
  • รายการองค์ประกอบที่สาม: (question if-true if-false)ที่questionเป็นคำถามใช่ / ไม่ใช่ปิดเป็นสตริงและif-trueและif-falseเป็นทรีย่อยที่เกี่ยวข้องกับคำถาม

Uฟังก์ชั่นการเดินและผลตอบแทนที่เป็นต้นไม้ที่มีการปรับเปลี่ยนอาจจะเป็น คำถามแต่ละข้อจะถูกถามโดยเริ่มจากรูทจนกระทั่งถึงลีฟในขณะที่โต้ตอบกับผู้ใช้

  • ค่าที่ส่งคืนสำหรับโหนดระดับกลาง(Q Y N)คือ(Q (U Y) N)(resp. (Q Y (U N))) หากคำตอบสำหรับคำถามQคือใช่ (resp. no )

  • ค่าที่ส่งคืนสำหรับ leaf คือ leaf เองหากโปรแกรมเดาคำตอบได้อย่างถูกต้องหรือ tree ที่กลั่นแล้วซึ่ง leaf ถูกแทนที่ด้วยคำถามและผลลัพธ์ที่เป็นไปได้สองแบบตามค่าที่นำมาจากผู้ใช้

ส่วนนี้ค่อนข้างตรงไปตรงมา ในการพิมพ์ซอร์สโค้ดเราใช้ตัวแปรตัวอ่านเพื่อสร้างโค้ดอ้างอิงตนเองโดยการตั้งค่า *PRINT-CIRCLE*เป็นจริงเราหลีกเลี่ยงการเรียกซ้ำแบบไม่สิ้นสุดระหว่างการพิมพ์แบบสวยเคล็ดลับเมื่อใช้WRITEกับ:print-circle Tคือฟังก์ชั่นอาจส่งกลับค่าไปยัง REPL ขึ้นอยู่กับว่าการเขียนเป็นรูปแบบสุดท้ายและดังนั้นถ้า REPL ไม่จัดการโครงสร้างแบบวงกลมเช่นนั้นจะถูกกำหนดโดยค่าเริ่มต้นมาตรฐานของ*PRINT-CIRCLE*จะมีการเรียกซ้ำแบบไม่สิ้นสุด เราต้องตรวจสอบให้แน่ใจว่าโครงสร้างวงกลมไม่ได้ถูกส่งกลับไปยัง REPL นั่นคือสาเหตุที่มี NIL อยู่ในตำแหน่งสุดท้ายของ LET วิธีนี้ช่วยลดปัญหาได้อย่างมาก


ดูดี! (y or n)ไม่จำเป็นต้อง แต่ผมอยากที่จะอนุญาตให้มันเป็นมันคือการปรับปรุง
abligh

@ ขอบคุณมาก เกี่ยวกับ y / n น่าจะช่วยได้และ IMHO นี้ไม่ได้ขัดแย้งกับ # 3 ซึ่งเกี่ยวกับการหลีกเลี่ยงการทำให้ข้อความสั้นลง
coredump

9

Python 2.7.6, 820 728 ไบต์

(อาจใช้ได้กับเวอร์ชั่นต่าง ๆ แต่ฉันไม่แน่ใจ)

def r(O,R):
 import sys,marshal as m;w=sys.stderr.write;i=sys.stdin.readline;t=O;w("OK, please think of something\n");u=[]
 def y(s):w(s);return i()[0]=='y'
 while t:
  if type(t)==str:
   if y("Is it %s?"%t):w("Good. That was soooo easy.")
   else:w("Oh. Well you win then -- What were you thinking of?");I=i().strip();w("Please give me a question about %s, so I can tell the difference between %s and %s"%(I,t,I));q=i().strip();a=y("What is the answer for %s?"%q);w("Thanks");p=[q,t];p.insert(a+1,I);z=locals();exec"O"+"".join(["[%s]"%j for j in u])+"=p"in z,z;O=z["O"]
   t=0
  else:u+=[y(t[0])+1];t=t[u[-1]]
 print"import marshal as m;c=%r;d=lambda:0;d.__code__=m.loads(c);d(%r,d)"%(m.dumps(R.__code__),O)
r('a pangolin',r)

มันไม่ได้สั้นไปกว่าคำตอบ Common LISP แต่นี่คือรหัสบางส่วน!


4

Python 3, 544 ไบต์

q="""
d=['a pangolin'];i=input;p=print
p("OK, Please think of something")
while len(d)!=1:
    d=d[1+(i(d[0])[0]=="n")]
x=i("Is it "+d[0]+"?")
if x[0]=="n":
    m=i("Oh. Well you win then -- What were you thinking of?")
    n=[i("Please give me a question about "+m+", so I can tell the difference between "+d[0]+" and "+m),*[[d[0]],[m]][::(i("What is the answer for "+m+"?")[0]=="n")*2-1]]
    p("Thanks")
    q=repr(n).join(q.split(repr(d)))
else:
    p("Good. That was soooo easy.")
q='q=""'+'"'+q+'""'+'"'+chr(10)+'exec(q)'
p(q)
"""
exec(q)

ลองออนไลน์!

คำถาม / คำตอบ / คำตอบจะถูกเก็บไว้ในอาร์เรย์โดยที่อาร์เรย์เก็บสามรายการ (เช่น ['Does it eat ants',['a pangolin'],['a dog']] ) จากนั้นจะได้รับคำตอบของคำถามและทำซ้ำโดยมีเพียงเนื้อหาของรายการที่สองหรือสามขึ้นอยู่กับคำตอบ เมื่อมันเข้าสู่อาเรย์ที่มีเพียงหนึ่งไอเท็มมันจะถามคำถามและเนื่องจากมันมีโคดซอร์สทั้งหมดของมันเป็นสตริงจึงสามารถใช้วิธีการเข้าร่วมแยกเพื่อแทรกส่วนขยายไปยังอาเรย์เพื่อเพิ่มสาขาใหม่ .

ตอนแรกฉันเขียนสิ่งนี้โดยไม่ตระหนักถึงความต้องการควินินดังนั้นจึงต้องอ่านคำถามใหม่อีกครั้งและต้องหาวิธีที่ฉันสามารถรันโค้ดและใช้งานเป็นสตริงได้ยาก แต่ในที่สุดฉันก็สะดุดกับความคิดของรูปแบบควินที่ดี

q="""
print("Some actual stuff")
q='q=""'+'"'+q+'""'+'"'+chr(10)+'exec()'
print(q)
"""
exec(q)

1

Python 3 , 497 ไบต์

t=["a pangolin"];c='''p=print;i=input;a=lambda q:i(q)[0]in"Yy"
def q(t):
  if len(t)<2:
    g=t[0]
    if a(f"Is it {g}?"):p("Good. That was soooo easy.")
    else:s=i("Oh. Well you win then -- What were you thinking of?");n=i(f"Please give me a question about {s}, so I can tell the difference between {s} and {g}.");t[0]=n;t+=[[g],[s]][::1-2*a(f"What is the answer for {s}?")];p("Thanks")
  else:q(t[2-a(t[0])])
p("Ok, please think of something");q(t);p(f"t={t};c=''{c!r}'';exec(c)")''';exec(c)

ค่อนข้างคล้ายกับคำตอบที่ไม่เป็นอันตรายสำหรับการแสดงต้นไม้ มันจะถามคำถามต่อไปซ้ำ ๆ ในขณะที่ลึกเข้าไปในรายการจนกว่าจะมีคำตอบเดียวเท่านั้น

เวอร์ชันที่ไม่ได้อัปโหลด

tree = ['a pangolin']

def ask(question):
  answer = input(question + '\n')
  if answer.lower() in ['yes', 'no']:
    return answer.lower() == 'yes'
  else:
    print('Please answer "yes" or "no".')
    return ask(question)
    
def query(tree):
  if len(tree) == 1:
    guess = tree.pop()
    if ask(f'Is it {guess}?'):
      print('Good. That was soooo easy.')
      tree.append(guess)
    else:
      thing = input('Oh. Well you win then -- What were you thinking of?\n')
      new_question = input(f'Please give me a question about {thing}, so I can tell the difference between {thing} and {guess}.\n')
      answer = ask(f'What is the answer for {thing}?')
      print('Thanks')
      tree.append(new_question)
      if answer:
        tree.append([thing])
        tree.append([guess])
      else:
        tree.append([guess])
        tree.append([thing])
  else:
    if ask(tree[0]):
      query(tree[1])
    else:
      query(tree[2])
      
while True:
  input('Ok, please think of something\n')
  query(tree)
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.