- Piet เป็นภาษาโปรแกรมเชิงลึกลับที่ทำให้โค้ดดูเหมือน ศิลปะนามธรรม โดยตั้งชื่อตาม Piet Mondrian ผู้บุกเบิกศิลปะนามธรรมเชิงเรขาคณิต
- โปรแกรมเป็นกราฟิกที่สร้างจากสีที่รู้จัก 20 สี และตัวรันจะเคลื่อนที่ระหว่างบล็อกสีพร้อมตีความ ปริมาณการเปลี่ยนสี เป็นคำสั่ง
- ข้อมูลทั้งหมดมีอยู่เป็นจำนวนเต็มเท่านั้นและถูกเก็บใน สแต็ก โดยขนาดของบล็อกสีคือค่า แต่จะไม่ถูกใส่ลงสแต็กโดยอัตโนมัติหากไม่มีคำสั่ง
push - การควบคุมการไหลถูกกำหนดด้วย Direction Pointer, Codel Chooser และกฎของบล็อกสีดำ·ขอบ·บล็อกสีขาว โดยพฤติกรรมบางอย่างอาจ แตกต่างกันไปตาม implementation
- มีตัวอย่างและระบบนิเวศของเครื่องมือภายนอก แต่ไม่มี interpreter ทางการที่เป็นแหล่งอ้างอิง และการจัดการข้อผิดพลาดกับการตีความสีที่ไม่เป็นมาตรฐานยังคงขึ้นกับ implementation
แนวคิดพื้นฐานของ Piet
- Piet เป็นภาษาโปรแกรมที่โค้ดโปรแกรมดูเหมือน ศิลปะนามธรรม
- ชื่อนี้มาจาก Piet Mondrian ผู้บุกเบิกศิลปะนามธรรมเชิงเรขาคณิต
- เดิมอยากใช้ชื่อ
Mondrianแต่มีภาษา scripting ชื่อเดียวกันอยู่แล้ว จึงใช้ชื่อ Piet - หลังจากเขียนสเปกขึ้นมา ก็เกิดชุมชนเล็ก ๆ ที่สร้างโปรแกรม, interpreter, IDE และ compiler กันมาอย่างต่อเนื่อง
- ไม่มี interpreter ทางการที่เป็นแหล่งอ้างอิง และ implementation ที่ใช้งานได้อาจตีความสเปกแตกต่างกันเล็กน้อย
- แม้มีคำอธิบายสเปกบางส่วนเพิ่มเติม แต่ implementation เดิมบางตัวอาจไม่ได้ทำตามคำอธิบายเหล่านี้
สีและหน่วยของโค้ด
- Piet ใช้สีทั้งหมด 20 สี
- สี 18 สีรวมอยู่ในวงจรสีและวงจรความสว่าง
- สีขาวและสีดำไม่รวมอยู่ในสองวงจรนี้
- วงจรสี มีลำดับคือ
red -> yellow -> green -> cyan -> blue -> magenta -> red - วงจรความสว่าง มีลำดับคือ
light -> normal -> dark -> lightlightยังถือได้ว่าเป็นระดับที่มืดกว่าdarkหนึ่งขั้น และในทางกลับกันก็เป็นจริงเช่นกัน
- สีที่ไม่เป็นมาตรฐาน เช่น สีส้ม หรือสีน้ำตาล สามารถใช้ได้ แต่ผลลัพธ์ขึ้นกับ implementation
- ในกรณีที่ง่ายที่สุด สีที่ไม่เป็นมาตรฐานจะถูกจัดการเหมือนสีขาว
- ความเป็นไปได้อีกอย่างคืออาจถูกจัดการเหมือนสีดำ
Codel และบล็อกสี
- โค้ด Piet คือกราฟิกที่ประกอบด้วยสีที่สามารถรู้จักได้
- เนื่องจากพิกเซลโค้ดแต่ละพิกเซลมีความหมายในเชิงภาษา ในโปรแกรมที่ขยายให้ดูง่าย พิกเซลเดี่ยวในโค้ดจึงเรียกว่า codel
- หน่วยการทำงานพื้นฐานคือ บล็อกสี
- บล็อกสีคือพื้นที่ที่ codel สีเดียวกันเชื่อมต่อกันในแนวบนล่างซ้ายขวา
- บล็อกที่แตะกันเฉพาะแนวทแยงไม่นับว่าเชื่อมต่อกัน
- บล็อกสีมีรูปร่างใดก็ได้ และอาจมีรูสีอื่นอยู่ภายในได้
- รูภายในไม่ถือเป็นส่วนหนึ่งของบล็อกนั้น
สแต็กและการแทนค่า
- Piet เก็บค่าข้อมูลทั้งหมดไว้ใน สแต็ก
- ค่าข้อมูลมีเฉพาะจำนวนเต็มเท่านั้น
- อาจรับเข้าหรือส่งออกเป็นค่าอักขระ Unicode ได้ตามคำสั่ง
- ในเชิงแนวคิด สแต็กมีความลึกไม่จำกัด แต่ implementation อาจกำหนดขนาดสแต็กสูงสุดที่จำกัดได้
- หากเกิด overflow ในสแต็กจำกัด จะเป็น runtime error และการจัดการขึ้นกับ implementation
- บล็อกสีที่ไม่ใช่สีดำและไม่ใช่สีขาวแทน ค่าจำนวนเต็ม เท่ากับจำนวน codel ในบล็อกนั้น
- จำนวนเต็มที่ไม่เป็นบวกไม่สามารถแทนได้โดยตรง
- แต่สามารถสร้างได้ผ่านตัวดำเนินการ
- ค่าของบล็อกสีจะไม่ถูก push ลงสแต็กโดยอัตโนมัติ จำเป็นต้องมีคำสั่ง
pushอย่างชัดเจน
- ขนาดของจำนวนเต็มก็ไม่จำกัดในเชิงแนวคิดเช่นกัน แต่ implementation อาจกำหนดขนาดจำนวนเต็มสูงสุดที่จำกัดได้
- integer overflow เป็น runtime error และการจัดการขึ้นกับ implementation
การไหลของการทำงาน
- interpreter เริ่มการทำงานจาก บล็อกสี ที่มี codel ซ้ายบนของโปรแกรม
- ระหว่างทำงานจะรักษาสถานะไว้สองอย่าง
- Direction Pointer(DP): ตอนแรกชี้ไปทางขวา และชี้ได้หนึ่งทิศจาก ขวา·ซ้าย·ลง·ขึ้น
- Codel Chooser(CC): ตอนแรกชี้ไปทางซ้าย และชี้ได้หนึ่งฝั่งจากซ้ายหรือขวา
- เป้าหมายการเคลื่อนที่ถัดไปถูกกำหนดจากขอบของบล็อกสีปัจจุบันและชุดค่ารวม DP·CC
- หาขอบของบล็อกสีปัจจุบันที่อยู่ไกลที่สุดในทิศ DP
- จากขอบนั้น เลือก codel ที่อยู่ไกลที่สุดในทิศ CC โดยอิงจากทิศทางการเคลื่อนที่ของ DP
- จาก codel นั้น เคลื่อนไปยังบล็อกสีที่ codel ถัดไปในทิศ DP สังกัดอยู่
- ทำขั้นตอนนี้ซ้ำจนถึงเงื่อนไขสิ้นสุด แล้วโปรแกรมจึงจบการทำงาน
บล็อกสีดำ, ขอบ และบล็อกสีขาว
- บล็อกสีดำ และขอบของโปรแกรมทำหน้าที่เป็นกำแพงที่ขวางการไหลของการทำงาน
- หาก interpreter พยายามเคลื่อนเข้าไปในบล็อกสีดำหรือออกนอกขอบ จะหยุดและสลับ CC
- หากความพยายามครั้งที่สองยังล้มเหลว จะหมุน DP ตามเข็มนาฬิกาหนึ่งขั้น
- หากสลับ CC และ DP สลับกันไปจนครบทั้งหมด 8 ครั้งแล้วยังออกจากบล็อกสีปัจจุบันไม่ได้ โปรแกรมจะจบการทำงาน
-
การเคลื่อนที่ผ่านบล็อกสีขาว
- บล็อกสีขาว เป็นพื้นที่อิสระที่ interpreter ผ่านไปได้โดยไม่มีสิ่งกีดขวาง
- เมื่อเคลื่อนจากบล็อกสีไปยังพื้นที่สีขาว interpreter จะเคลื่อนเป็นเส้นตรงในทิศ DP จนถึงบล็อกสีที่ไม่ใช่สีขาว
- เมื่อผ่านบล็อกสีขาวไปยังสีใหม่ จะไม่มีคำสั่งใดถูกเรียกใช้
- ด้วยคุณสมบัตินี้ บล็อกสีขาวจึงมีประโยชน์ในการเขียนลูป เพราะสามารถเปลี่ยนสีปัจจุบันได้โดยไม่ต้องเรียกใช้คำสั่ง
- การเคลื่อนที่ผ่านบล็อกสีขาวไม่ใช้ขั้นตอนเลือกทางออกจากบล็อกสีที่ไม่ใช่สีขาว แต่ทำเพียง เคลื่อนที่เป็นเส้นตรง เท่านั้น
-
เมื่อติดอยู่ในบล็อกสีขาว
- หากเคลื่อนเป็นเส้นตรงผ่านบล็อกสีขาวแล้วพบกับบล็อกสีดำหรือขอบ จะถือว่าถูกจำกัดไว้
- ในกรณีนี้จะสลับ CC แต่เนื่องจากตำแหน่งที่ต้องการเคลื่อนไปไม่เปลี่ยน จึงหมุน DP ตามเข็มนาฬิกาหนึ่งขั้นทันที
- จากนั้นเริ่มเคลื่อนเป็นเส้นตรงอีกครั้งจาก codel สีขาวปัจจุบันในทิศ DP ใหม่
- ทุกครั้งที่พบข้อจำกัดภายในบล็อกสีขาว จะทำซ้ำการสลับ CC และการหมุน DP
- เมื่อเข้าสู่บล็อกสี การทำงานจะดำเนินต่อไป และหากเริ่มย้อนเส้นทางภายในบล็อกสีขาว แสดงว่าไม่มีทางออกและการทำงานจะจบลง
ระบบคำสั่ง
- คำสั่งของ Piet ถูกกำหนดจาก การเปลี่ยนสี เมื่อเคลื่อนจากบล็อกสีหนึ่งไปยังบล็อกสีถัดไป
- จำนวนขั้นที่เคลื่อนไปในวงจรสีและจำนวนขั้นที่เคลื่อนไปในวงจรความสว่างจะกำหนดคำสั่ง
- ในการเปลี่ยนสีที่เกิดจากการเคลื่อนผ่านบล็อกสีขาว จะไม่มีคำสั่งถูกเรียกใช้
-
คำสั่งสแต็กและเลขคณิต
push: ใส่ค่าของบล็อกสีที่เพิ่งออกมาลงในสแต็กpop: ดึงค่าบนสุดของสแต็กออกแล้วทิ้งadd: บวกค่าสองตัวบนสุด แล้วใส่ผลลัพธ์กลับลงสแต็กsubtract: ใส่ผลลัพธ์ของการนำค่าบนสุดไปลบออกจากค่าลำดับที่สองลงในสแต็กmultiply: คูณค่าสองตัวบนสุดdivide: ทำหารจำนวนเต็ม โดยนำค่าลำดับที่สองหารด้วยค่าบนสุด- การหารด้วย 0 เป็นข้อผิดพลาดที่ขึ้นกับ implementation และแนะนำให้จัดการโดยละเว้นคำสั่ง
mod: ใส่ เศษเหลือ จากการนำค่าลำดับที่สองหารด้วยค่าบนสุดลงในสแต็ก- ผลลัพธ์มีเครื่องหมายเดียวกับตัวหาร หรือก็คือค่าบนสุดของสแต็ก
- หากค่าบนสุดเป็น 0 จะเป็นข้อผิดพลาดจากการหารด้วย 0 และแนะนำให้จัดการโดยละเว้นคำสั่ง
modสำหรับตัวตั้งที่เป็นลบเหมือนกับ floored division ที่กล่าวถึงใน modulus operation ของ Wikipedia
-
คำสั่งเปรียบเทียบ, pointer และอินพุต/เอาต์พุต
not: เปลี่ยนค่าบนสุดของสแต็กเป็น 0 หากไม่ใช่ 0 และเป็น 1 หากเป็น 0greater: ใส่ 1 ลงสแต็กหากค่าลำดับที่สองมากกว่าค่าบนสุด มิฉะนั้นใส่ 0pointer: ดึงค่าบนสุดของสแต็กออก แล้วหมุน DP ตามเข็มนาฬิกาตามจำนวนนั้น- หากเป็นลบ ให้หมุนทวนเข็มนาฬิกา
switch: ดึงค่าบนสุดของสแต็กออก แล้วสลับ CC ตามจำนวนนั้น- หากเป็นลบ ให้สลับตามค่าสัมบูรณ์
duplicate: ใส่สำเนาของค่าบนสุดของสแต็กลงในสแต็กroll: ดึงค่าสองตัวบนสุดออก แล้วหมุนบางส่วนของสแต็กที่เหลือตามความลึกและจำนวนครั้งที่กำหนด- หากความลึกเป็นลบ จะเป็นข้อผิดพลาดและคำสั่งจะถูกละเว้น
rollที่เกินความลึกสแต็กสูงสุดซึ่งขึ้นกับ implementation เป็นข้อผิดพลาดที่ขึ้นกับ implementation และแนะนำให้ละเว้นคำสั่งin: อ่านตัวเลขหรืออักขระจาก STDIN แล้วใส่ลงสแต็ก- หากไม่มีอินพุต หรือไม่ได้รับจำนวนเต็มในการรับอินพุตจำนวนเต็ม จะเป็นข้อผิดพลาดและคำสั่งจะถูกละเว้น
out: ส่งออกค่าบนสุดของสแต็กเป็นตัวเลขหรืออักขระไปยัง STDOUT- การดำเนินการที่ทำไม่ได้เพราะมีค่าในสแต็กไม่พอ จะถูกละเว้น และไปยังคำสั่งถัดไป
ตัวอย่างและเครื่องมือ
- ตัวอย่าง Piet ดูได้ที่ Sample programs
- interpreter และเครื่องมือพัฒนาภายนอกถูกรวบรวมไว้ที่ Third-party Piet interpreters and development tools
1 ความคิดเห็น
ความคิดเห็นจาก Hacker News
โปรแกรมสุดท้ายในหน้าตัวอย่างน่าทึ่งจริง ๆ: มีคนชื่อ Piet เห็นงานศิลปะที่ทำให้นึกถึงภาษา Piet แล้วลองนำไปรันดู
มันรันได้ และอาจเป็นกรณีแรกในประวัติศาสตร์ที่ศิลปินกราฟิกวาด โปรแกรมคอมพิวเตอร์ที่ทำงานได้โดยบังเอิญ
https://www.dangermouse.net/esoteric/piet/samples.html
https://gitlab.fabcity.hamburg/hofalab/piet-get-together
https://www.mcmillen.dev/sigbovik/
Piet ถ่ายรูปผลงาน แปลงเป็นไฟล์ภาพที่จัดสีให้ใกล้กับพาเลตของ Piet แล้วรันดู ปรากฏว่ามันรันได้จริง และโค้ดเป็นลูปไม่รู้จบที่อ่านอักขระ ASCII แล้วพิมพ์ ค่าตัวเลข ASCII ของอักขระนั้นออกมา
เรื่องนี้แทบไม่น่าเชื่อจริง ๆ
คำอธิบายที่ว่า “แน่นอนว่าถ้าใช้โปรแกรมที่ใหญ่ขึ้น ก็จะได้ค่าที่แม่นยำขึ้น” สำหรับผมให้ความรู้สึกเหมือนมุกตลกแบบที่ไม่เคยเห็นมาก่อน
ตามสเปกแล้ว interpreter ควรเริ่มไถลจาก codel สีขาวปัจจุบันไปตาม DP ทิศทางใหม่ และเดินหน้าต่อไปจนกว่าจะเข้าไปในบล็อกสีหรือเจอข้อจำกัดอื่น
แต่ interpreter ของ npiet จะมองเข้าไปในพื้นที่ว่างแล้วกรอกลับไปยังตำแหน่ง codel สีสุดท้าย สักวันหนึ่งผมอยากใส่พฤติกรรมนั้นเป็นตัวเลือกใน lexer ของคอมไพเลอร์ Piet ของผม แต่ยังไม่ได้แตะเลย
ถ้าทำตามสเปก โปรแกรมนั้นจะกลายเป็นลูปไม่จบแบบง่าย ๆ เพราะมุมสุดขอบของแทบทุกบล็อกอยู่ติดกับสีขาว การเขียนโปรแกรม Piet ที่ซับซ้อนให้ใช้ได้กับ interpreter และคอมไพเลอร์หลายตัวค่อนข้างยาก และแต่ละตัวก็มีความแตกต่างในการตีความเล็ก ๆ น้อย ๆ ที่ไม่ได้บันทึกไว้
ผมคิดว่าเอาต์พุตจากแบ็กเอนด์ Piet ของผมโดยทั่วไปพึ่งพา interpreter น้อยกว่า แต่ที่ขุดดูละเอียด ๆ ก็มีแค่ interpreter อื่น ๆ สักสามสี่ตัวเท่านั้น
https://github.com/boothby/repiet/
จากการอ่านเอกสารคร่าว ๆ เงื่อนไขที่ว่า “การดำเนินการที่ทำไม่ได้ เช่น คำสั่งที่ pop ไม่ได้เพราะมีค่าในสแต็กไม่พอ จะถูกละเว้นแล้วไปทำคำสั่งถัดไปต่อ” ทำให้ภาพแบบนี้ทั้งหมดอาจรันได้โดยไม่มีข้อผิดพลาดก็ได้
แต่ในบรรดาภาพสุ่มเหล่านั้น จะมีสักกี่ภาพที่ทำสิ่งที่ “มีความหมาย” จริง ๆ นั่นเป็นอีกเรื่องหนึ่ง
Piet เป็นการทดลองที่เหมือนหมุดหมายสำคัญในบรรดาภาษาโปรแกรมแนวลึกลับ แต่ผมคิดว่ามันยังไปไม่ถึงเป้าหมายที่จะทำให้โปรแกรมดูเหมือน ภาพวาดของ Mondrian เว้นแต่นักพัฒนาจะตั้งใจทำอย่างนั้นจริง ๆ
ผมอยากให้โครงสร้างของภาษาเองถูกออกแบบมาให้ไม่ว่าจะ “เขียน” อะไร ก็ดูเหมือนภาพวาดของ Mondrian
มักมีคำถามแบบนี้ผุดขึ้นมาเสมอ: อัลกอริทึมหน้าตาเป็นอย่างไร?
เราจะสร้างบางสิ่งในโลกจริงที่คล้ายกับสิ่งที่อยู่ในนิยายของ Herman Hesse เรื่อง The Glass Bead Game ได้ไหม? ชื่อต้นฉบับคือ Magister Ludi
ในฐานะคนที่โน้มเอียงไปทางการมองเห็น ผมอยากเชื่อว่าเป็นไปได้ และจริง ๆ ก็เคยลองใช้เครื่องมือแบบนั้นมาแล้ว
https://community.carbide3d.com/uploads/default/original/3X/5/b/5b0872a5666fec9b7bb6fd623c431de03263372d.jpeg
แต่ถ้าไม่มีคำตอบที่ชัดเจนต่อคำถามข้างต้น เครื่องมือเหล่านี้ก็เสี่ยงจะกลายเป็นแบบต่อไปนี้อยู่เสมอ
https://blueprintsfromhell.tumblr.com/
https://scriptsofanotherdimension.tumblr.com/
การหาสมดุลระหว่างพลังในการสื่อด้วยภาพกับความเป็นโมดูลก็ยาก และถ้าผลักดันความเป็นโมดูลมากไป ก็กลับไปชน กำแพงข้อความ ที่พยายามหลีกเลี่ยงได้ง่ายเกินไป
Sergei Lewis กับผมต่างก็ทำเครื่องมือสำหรับสร้างโค้ด Piet ขึ้นมา แอสเซมเบลอร์ของ Sergei สร้างโค้ดที่ดูดีกว่าแบ็กเอนด์ Piet ของผมมาก
สิ่งที่เห็นได้จริงจากเอาต์พุตของคอมไพเลอร์ผมก็คือ ผมใช้ trampoline แบบขี้เกียจมากจริง ๆ
http://www.toothycat.net/wiki/wiki.pl?MoonShadow/Piet
https://github.com/boothby/repiet/
https://en.wikipedia.org/wiki/Trampoline_(computing)
เป็นไอเดียที่ได้มาจากการอ่านหนังสือของ Steven Pinker เขาบอกว่าคำเชิงนามธรรมสามารถแตกออกเป็นคำที่เรียบง่ายกว่า และท้ายที่สุดก็จะอธิบายความสัมพันธ์เชิงพื้นที่บางอย่าง เช่น “rekindle” อาจมองได้ว่าเป็น “นำสองสิ่งกลับมารวมกันอีกครั้ง”
ในทำนองเดียวกัน for loop ก็เป็นแนวคิดทางจิตใจแบบ “สิ่งหนึ่งเคลื่อนผ่านสิ่งอื่นหลาย ๆ อย่าง” และมีการแทนด้วยภาพอย่าง “100” -> “010” -> “001”
ถ้าอย่างนั้นก็สงสัยว่าจะสร้างภาษาที่นิยามองค์ประกอบเหล่านี้เป็น การแปลงเชิงภาพ ล้วน ๆ ได้หรือไม่
ของแบบนี้เหมาะจะไปอยู่ในหนังสืบสวนระทึกขวัญ ให้พระเอกหรือนักสืบตันอยู่พักหนึ่ง ก่อนที่ใครสักคนจะรู้ว่านั่นคือโค้ด
ทั้งที่เคยคิดว่ามีแค่ QR code เท่านั้นที่มีประโยชน์
มีคนสร้าง quine ด้วย Piet: http://mamememo.blogspot.com/2009/10/piet-quine.html?m=1
รูปภาพในโพสต์นั้นเสียแล้ว แต่มีสำเนาอยู่ที่นี่: https://codegolf.stackexchange.com/a/23255/103045
ช่วงเวลาที่ได้ค้นพบ Piet เป็นช่วงเวลาพิเศษที่ผสมทั้งความตื่นตะลึง ความสับสน และความประหลาดใจ
ในกรณีของผม มันอยู่ในบทสนทนานี้จากพอดแคสต์วิทยาการคอมพิวเตอร์ “The CS Primer Show” ที่ทำกับเพื่อนชื่อ Oz: https://show.csprimer.com/episodes/e2-dont-let-a-gpt-have-all-the-fun
ตอนมหาวิทยาลัยมีคลาสเล็ก ๆ ที่ว่าด้วย ภาษาโปรแกรมแนว esoteric
แต่ละคนต้องเลือกภาษาอย่าง Brainfuck, Piet หรือภาษาแนวนี้มาลองเล่น และผมเลือก Piet ซึ่งก็สนุกพอสมควร
พูดตรง ๆ แอปตัวอย่างเล็ก ๆ ที่ผมทำไม่ได้ยอดเยี่ยมด้านความสวยงามนัก และถ้าจะสร้างงานศิลป์ด้วย Piet ก็คงต้องเป็นผู้เชี่ยวชาญ Piet เลย
หน้าตัวอย่างยอดเยี่ยมมาก
เห็นได้เลยว่าแคนวาสค่อย ๆ พัฒนาให้ซับซ้อนและดูดีขึ้นเรื่อย ๆ อย่างไร
https://www.dangermouse.net/esoteric/piet/samples.html
ที่บอกว่า “light ถือว่า มืดกว่า dark หนึ่งขั้น” นี่ค่อนข้างลึกซึ้งทีเดียว
ถ้าสร้าง autoencoder ที่เรียนรู้วิธีรับโค้ดจาก Python หรือภาษาอื่นที่ไม่ esoteric เท่า แล้วส่งออกเป็น Piet ได้ ก็น่าจะเจ๋งมาก
แบบนั้นอาจสร้างอัลกอริทึมแบบสุ่มได้คล้าย ๆ Stable Diffusion เลย