4 điểm bởi GN⁺ 2024-04-25 | 1 bình luận | Chia sẻ qua WhatsApp
  • Piet là một ngôn ngữ lập trình bí truyền được tạo ra để mã trông như tranh trừu tượng, được đặt theo tên Piet Mondrian, người tiên phong của nghệ thuật trừu tượng hình học
  • Chương trình là đồ họa được tạo từ 20 màu được nhận diện; trình thực thi di chuyển giữa các khối màu và diễn giải mức thay đổi màu thành lệnh
  • Mọi dữ liệu chỉ tồn tại dưới dạng số nguyên và được lưu trong stack; kích thước của khối màu là giá trị, nhưng không tự động được đưa lên stack nếu không có lệnh push
  • Điều khiển luồng được quyết định bởi Direction Pointer và Codel Chooser, cùng các quy tắc về khối đen, cạnh và khối trắng; một số hành vi có thể khác nhau tùy triển khai
  • Có ví dụ và hệ sinh thái công cụ bên ngoài, nhưng không có trình thông dịch chính thức có thẩm quyền; xử lý lỗi và cách diễn giải màu không chuẩn vẫn phụ thuộc vào triển khai

Ý tưởng cơ bản của Piet

  • Piet là một ngôn ngữ lập trình mà mã chương trình trông như nghệ thuật trừu tượng
  • Tên gọi được lấy từ Piet Mondrian, người tiên phong của nghệ thuật trừu tượng hình học
  • Ban đầu muốn dùng tên Mondrian, nhưng vì đã có một ngôn ngữ kịch bản cùng tên nên đã dùng tên Piet
  • Sau khi đặc tả được viết, một cộng đồng nhỏ đã hình thành và tạo ra các chương trình, trình thông dịch, IDE và trình biên dịch
  • Không có trình thông dịch chính thức có thẩm quyền; các triển khai hiện có có thể diễn giải đặc tả hơi khác nhau
  • Một số phần giải thích đặc tả đã được bổ sung, nhưng có khả năng một số triển khai hiện tại không tuân theo chúng

Màu sắc và đơn vị mã

  • Piet dùng tổng cộng 20 màu
    • 18 màu thuộc vòng tuần hoàn màu và vòng tuần hoàn độ sáng
    • Màu trắng và màu đen không thuộc hai vòng tuần hoàn này
  • Vòng tuần hoàn màu có thứ tự red -> yellow -> green -> cyan -> blue -> magenta -> red
  • Vòng tuần hoàn độ sáng có thứ tự light -> normal -> dark -> light
    • light cũng được xem là tối hơn dark một bước, và điều ngược lại cũng đúng
  • Có thể dùng các màu không chuẩn như cam hoặc nâu, nhưng hiệu ứng phụ thuộc vào triển khai
    • Trong trường hợp đơn giản nhất, màu không chuẩn được xử lý như màu trắng
    • Một khả năng khác là chúng có thể được xử lý như màu đen

Codel và khối màu

  • Mã Piet là một hình ảnh đồ họa gồm các màu có thể nhận diện
  • Vì từng pixel mã riêng lẻ có ý nghĩa về mặt ngôn ngữ, trong các chương trình được phóng to để dễ nhìn, một pixel đơn trong mã được gọi là codel
  • Đơn vị thực thi cơ bản là khối màu
    • Khối màu là vùng gồm các codel cùng màu nối với nhau theo trên-dưới-trái-phải
    • Các khối chỉ chạm nhau theo đường chéo không được xem là nối liền
    • Khối màu có thể có bất kỳ hình dạng nào và có thể chứa các lỗ màu khác bên trong
    • Các lỗ bên trong không phải là một phần của khối đó

Stack và biểu diễn giá trị

  • Piet lưu mọi giá trị dữ liệu trong stack
  • Giá trị dữ liệu chỉ có số nguyên
    • Tùy lệnh, có thể nhập hoặc xuất dưới dạng giá trị ký tự Unicode
  • Về mặt khái niệm, stack có độ sâu vô hạn, nhưng triển khai có thể đặt kích thước stack tối đa hữu hạn
  • Nếu xảy ra tràn trên stack hữu hạn thì đó là lỗi runtime, và cách xử lý phụ thuộc vào triển khai
  • Các khối màu không phải đen và trắng biểu thị một giá trị số nguyên bằng số lượng codel trong khối đó
    • Không thể biểu diễn trực tiếp số nguyên không dương
    • Tuy nhiên có thể tạo ra chúng thông qua toán tử
    • Giá trị của khối màu không tự động được push lên stack; cần lệnh push tường minh
  • Về mặt khái niệm, kích thước số nguyên cũng là vô hạn, nhưng triển khai có thể đặt kích thước số nguyên tối đa hữu hạn
    • Tràn số nguyên là lỗi runtime và cách xử lý phụ thuộc vào triển khai

Luồng thực thi

  • Trình thông dịch bắt đầu thực thi tại khối màu chứa codel ở góc trên bên trái của chương trình
  • Trong khi thực thi, hai trạng thái được duy trì
    • Direction Pointer(DP): ban đầu chỉ sang phải, và có thể chỉ một trong các hướng phải, trái, xuống, lên
    • Codel Chooser(CC): ban đầu chỉ sang trái, và có thể chỉ một trong hai phía trái hoặc phải
  • Đích di chuyển tiếp theo được quyết định bởi biên của khối màu hiện tại và tổ hợp DP·CC
    • Tìm cạnh của khối màu hiện tại xa nhất theo hướng DP
    • Trên cạnh đó, chọn codel xa nhất theo hướng CC, tính theo hướng tiến của DP làm chuẩn
    • Từ codel đó, di chuyển sang khối màu chứa codel ngay kế tiếp theo hướng DP
  • Lặp lại quá trình này cho đến khi đạt điều kiện kết thúc thì chương trình kết thúc

Khối đen, cạnh và khối trắng

  • Khối màu đen và cạnh của chương trình hoạt động như các rào chắn chặn luồng thực thi
  • Nếu trình thông dịch cố di chuyển vào khối đen hoặc ra ngoài cạnh, nó dừng lại và chuyển đổi CC
  • Nếu lần thử thứ hai cũng thất bại, DP được xoay một bước theo chiều kim đồng hồ
  • Sau khi luân phiên đổi CC và DP tổng cộng 8 lần mà vẫn không thể rời khỏi khối màu hiện tại, chương trình kết thúc
  • Di chuyển qua khối trắng

    • Khối màu trắng là vùng tự do mà trình thông dịch đi qua không bị cản trở
    • Khi di chuyển từ một khối màu vào vùng trắng, trình thông dịch đi thẳng theo hướng DP cho đến khi chạm tới một khối màu không trắng
    • Khi đi qua khối trắng để sang màu mới, không có lệnh nào được thực thi
    • Nhờ đặc tính này, khối trắng hữu ích cho việc viết vòng lặp vì có thể thay đổi màu hiện tại mà không thực thi lệnh
    • Di chuyển qua khối trắng không dùng quy trình chọn lối ra từ khối màu không trắng, mà chỉ thực hiện di chuyển thẳng
  • Khi bị chặn trong khối trắng

    • Nếu đang đi thẳng qua khối trắng mà gặp khối đen hoặc cạnh, điều đó được xử lý như gặp hạn chế
    • Trong trường hợp này, CC được chuyển đổi nhưng vị trí muốn di chuyển không thay đổi, nên DP lập tức được xoay một bước theo chiều kim đồng hồ
    • Sau đó, từ codel trắng hiện tại, lại tiếp tục đi thẳng theo hướng DP mới
    • Mỗi khi gặp hạn chế bên trong khối trắng, việc chuyển đổi CC và xoay DP được lặp lại
    • Khi đi vào một khối màu thì thực thi tiếp tục; nếu bắt đầu lần theo đường cũ trong khối trắng thì không còn lối ra và thực thi kết thúc

Hệ thống lệnh

  • Lệnh của Piet được quyết định bởi thay đổi màu khi di chuyển từ một khối màu sang khối màu tiếp theo
  • Số bước đã di chuyển trong vòng tuần hoàn màu và số bước đã di chuyển trong vòng tuần hoàn độ sáng quyết định lệnh
  • Không có lệnh nào được thực thi trong chuyển đổi màu đi qua khối trắng
  • Lệnh stack và số học

    • push: đưa giá trị của khối màu vừa rời khỏi lên stack
    • pop: lấy và bỏ giá trị trên đỉnh stack
    • add: cộng hai giá trị trên cùng và đưa kết quả trở lại stack
    • subtract: đưa lên stack kết quả của giá trị thứ hai trừ đi giá trị trên đỉnh
    • multiply: nhân hai giá trị trên cùng
    • divide: chia nguyên giá trị thứ hai cho giá trị trên đỉnh
    • Chia cho 0 là lỗi phụ thuộc triển khai, và cách được khuyến nghị là bỏ qua lệnh
    • mod: đưa lên stack phần dư khi giá trị thứ hai được chia cho giá trị trên đỉnh
    • Kết quả có cùng dấu với số chia, tức giá trị trên đỉnh stack
    • Nếu giá trị trên đỉnh là 0 thì đó là lỗi chia cho 0, và cách được khuyến nghị là bỏ qua lệnh
    • mod với số bị chia âm giống với phép chia lấy sàn được mô tả trong modulus operation trên Wikipedia
  • Lệnh so sánh, con trỏ và nhập/xuất

    • not: nếu giá trị trên đỉnh stack khác 0 thì đổi thành 0, nếu là 0 thì đổi thành 1
    • greater: nếu giá trị thứ hai lớn hơn giá trị trên đỉnh thì đưa 1 lên stack, nếu không thì 0
    • pointer: lấy giá trị trên đỉnh stack ra và xoay DP theo chiều kim đồng hồ số lần tương ứng
    • Nếu là số âm thì xoay ngược chiều kim đồng hồ
    • switch: lấy giá trị trên đỉnh stack ra và chuyển đổi CC số lần tương ứng
    • Nếu là số âm thì chuyển đổi theo trị tuyệt đối
    • duplicate: đưa một bản sao của giá trị trên đỉnh stack lên stack
    • roll: lấy hai giá trị trên cùng ra và xoay một phần stack còn lại theo độ sâu và số lần được chỉ định
    • Nếu độ sâu là số âm thì đó là lỗi và lệnh bị bỏ qua
    • roll vượt quá độ sâu stack tối đa phụ thuộc triển khai là lỗi phụ thuộc triển khai, và khuyến nghị bỏ qua lệnh
    • in: đọc một số hoặc ký tự từ STDIN và đưa lên stack
    • Nếu không có input, hoặc khi input số nguyên mà không nhận được số nguyên, thì đó là lỗi và lệnh bị bỏ qua
    • out: xuất giá trị trên đỉnh stack ra STDOUT dưới dạng số hoặc ký tự
    • Các phép toán không thể thực hiện do thiếu giá trị trên stack sẽ bị bỏ qua và chuyển sang lệnh tiếp theo

Ví dụ và công cụ

1 bình luận

 
GN⁺ 2024-04-25
Ý kiến trên Hacker News
  • Chương trình cuối cùng trong trang ví dụ thực sự đáng kinh ngạc: có người tên là Piet đã nhìn thấy một tác phẩm nghệ thuật gợi nhớ đến ngôn ngữ Piet rồi thử chạy nó.
    Nó đã chạy được, và có lẽ đây là trường hợp đầu tiên trong lịch sử một họa sĩ đồ họa vô tình vẽ ra một chương trình máy tính hoạt động.
    https://www.dangermouse.net/esoteric/piet/samples.html
    https://gitlab.fabcity.hamburg/hofalab/piet-get-together

    • Nếu nới lỏng đủ điều kiện của chữ “hoạt động”, thì từng có lúc người ta chỉ ra rằng phần lớn các vệt sơn bắn tóe vốn đã là những chương trình Perl hợp lệ.
      https://www.mcmillen.dev/sigbovik/
    • Piet J. đang xem tác phẩm trong một phòng tranh nhỏ và cảm thấy nó giống một chương trình Piet; tác giả nói rằng mình hoàn toàn không biết ngôn ngữ đó.
      Piet chụp ảnh tác phẩm, chuyển nó thành tệp hình ảnh với các màu được hiệu chỉnh gần với bảng màu Piet, rồi chạy thử và nó thực sự chạy được; đoạn mã là một vòng lặp vô hạn đọc ký tự ASCII và in ra giá trị số ASCII tương ứng.
      Điều này thật khó tin.
    • Ví dụ tính π cũng rất hay.
      Câu giải thích “dĩ nhiên, nếu viết chương trình lớn hơn thì sẽ có được giá trị chính xác hơn” nghe với tôi như một kiểu đùa mà trước giờ tôi chưa từng thấy.
    • Đáng tiếc là chuyện đó phụ thuộc vào sự khác biệt giữa npiet và đặc tả Piet hiện tại.
      Theo đặc tả, trình thông dịch phải bắt đầu trượt theo DP hướng mới từ codel trắng hiện tại, và tiếp tục cho đến khi đi vào một khối màu hoặc gặp ràng buộc khác.
      Nhưng trình thông dịch npiet lại nhìn qua khoảng trắng rồi tua ngược về vị trí codel màu cuối cùng. Tôi vẫn muốn một ngày nào đó thêm hành vi đó thành tùy chọn trong lexer của trình biên dịch Piet của mình, nhưng đến giờ vẫn chưa động vào.
      Nếu theo đúng đặc tả, chương trình đó sẽ trở thành một vòng lặp không kết thúc khá đơn giản vì các góc cực trị của gần như mọi khối đều kề với màu trắng. Viết các chương trình Piet phức tạp nhắm đến nhiều trình thông dịch và trình biên dịch là việc khá khó, và mỗi cái đều có những khác biệt diễn giải tinh vi không được tài liệu hóa.
      Tôi nghĩ đầu ra từ backend Piet của mình nhìn chung ít phụ thuộc vào trình thông dịch hơn, nhưng tôi cũng chỉ đào sâu với khoảng ba bốn trình thông dịch khác thôi.
      https://github.com/boothby/repiet/
    • Tôi tò mò xác suất để những bức hình đơn giản kiểu này, tức chỉ gồm vài khối chữ nhật lớn, trở thành một chương trình hợp lệ là bao nhiêu.
      Lướt qua tài liệu thì có vẻ với điều kiện “những thao tác không thể thực hiện, như phép toán cần pop khi không đủ giá trị trên stack, sẽ פשוט bị bỏ qua và tiếp tục sang lệnh tiếp theo”, những hình ảnh kiểu này có thể đều chạy mà không lỗi.
      Tuy vậy, trong số các hình ngẫu nhiên như thế, có bao nhiêu cái thực sự làm được điều gì đó “có ý nghĩa” lại là chuyện khác.
  • Piet là một thử nghiệm mang tính cột mốc ngay cả trong thế giới ngôn ngữ lập trình kỳ quặc, nhưng tôi thấy nó chưa đạt được mục tiêu làm cho chương trình trông như một bức tranh Mondrian trừ khi nhà phát triển thực sự cố ý như vậy.
    Tôi ước chính cấu trúc của ngôn ngữ được thiết kế để bất kể “viết” gì thì kết quả cũng trông như tranh Mondrian.

    • Đúng vậy, nhưng Mondrian về cơ bản chủ yếu chỉ dùng các màu cơ bản, nên chắc hẳn sẽ khá hạn chế.
  • Tôi luôn hay nảy ra câu hỏi này: thuật toán trông như thế nào?
    Liệu có thể tạo ra ngoài đời thực thứ gì đó giống với những gì xuất hiện trong tiểu thuyết The Glass Bead Game của Hermann Hesse không? Tựa gốc là Magister Ludi
    Là một người thiên về thị giác, tôi muốn tin là có thể, và thực tế cũng đã từng thử dùng những công cụ như vậy
    https://community.carbide3d.com/uploads/default/original/3X/5/b/5b0872a5666fec9b7bb6fd623c431de03263372d.jpeg
    Nhưng nếu không có câu trả lời rõ ràng cho câu hỏi trên, những công cụ kiểu này luôn có nguy cơ trở thành như sau
    https://blueprintsfromhell.tumblr.com/
    https://scriptsofanotherdimension.tumblr.com/
    Cũng khó cân bằng giữa khả năng biểu đạt bằng hình ảnh và tính mô-đun, mà nếu đẩy mạnh tính mô-đun thì lại quá dễ quay về rào cản văn bản vốn muốn tránh

    • Khi tự tay viết Piet, có một niềm vui trong việc khám phá “thuật toán trông như thế nào”
      Sergei Lewis và tôi mỗi người đều đã làm công cụ tạo mã Piet. Trình assembler của Sergei tạo ra mã đẹp mắt hơn hẳn backend Piet của tôi
      Điều thực sự thấy được trong đầu ra của trình biên dịch của tôi chỉ là tôi đã dùng trampoline một cách cực kỳ lười biếng
      http://www.toothycat.net/wiki/wiki.pl?MoonShadow/Piet
      https://github.com/boothby/repiet/
      https://en.wikipedia.org/wiki/Trampoline_(computing)
    • Tôi tin rằng bất kỳ thuật toán nào, thậm chí bất kỳ khái niệm tinh thần nào, cũng có quan hệ 1:1 với một biểu đạt trực quan
      Đây là ý tưởng tôi có được khi đọc sách của Steven Pinker: từ ngữ trừu tượng có thể được tách thành những từ đơn giản hơn, và cuối cùng sẽ mô tả một dạng quan hệ không gian nào đó. Ví dụ, “rekindle” có thể được hiểu là “đưa hai thứ lại với nhau một lần nữa”
      Tương tự, vòng lặp for cũng là một khái niệm tinh thần về việc “một thứ đi qua nhiều thứ khác”, và điều này có biểu đạt trực quan như “100” -> “010” -> “001”
      Vì vậy tôi tự hỏi liệu có thể tạo ra một ngôn ngữ định nghĩa những thành phần như thế này bằng các biến đổi trực quan thuần túy hay không
    • Với những chương trình đơn giản, cũng có thể hình dung việc hiện thực một máy Turing dùng màu sắc làm ký hiệu
  • Mấy thứ như thế này rất hợp để xuất hiện trong phim trinh thám tội phạm, làm nhân vật chính hoặc điều tra viên bế tắc một lúc trước khi ai đó nhận ra đó là mã
    Tôi từng nghĩ chỉ có mã QR mới hữu dụng theo kiểu đó

  • Có người đã tạo quine bằng Piet: http://mamememo.blogspot.com/2009/10/piet-quine.html?m=1
    Ảnh trong bài đó đã bị hỏng, nhưng có bản sao ở đây: https://codegolf.stackexchange.com/a/23255/103045

  • Khoảnh khắc phát hiện ra Piet là một trải nghiệm đặc biệt pha trộn giữa cảm giác kính nể, bối rối và ngạc nhiên
    Với tôi, điều đó được ghi lại trong cuộc trò chuyện này trên podcast khoa học máy tính “The CS Primer Show” với người bạn Oz của tôi: https://show.csprimer.com/episodes/e2-dont-let-a-gpt-have-all-the-fun

  • Ở đại học có một lớp học nhỏ về ngôn ngữ lập trình kỳ lạ
    Mỗi người phải chọn một ngôn ngữ như Brainfuck, Piet rồi nghịch thử, và tôi chọn Piet, thấy khá thú vị
    Thành thật mà nói, ứng dụng mẫu nhỏ tôi làm ra không thật sự xuất sắc về mặt thẩm mỹ, và có vẻ muốn tạo nghệ thuật bằng Piet thì phải trở thành chuyên gia Piet

  • Trang ví dụ rất tuyệt
    Có thể thấy canvas dần tiến hóa để ngày càng tinh vi và đẹp mắt hơn
    https://www.dangermouse.net/esoteric/piet/samples.html

  • Câu “light được coi là tối hơn dark một bậc” nghe khá thâm sâu

  • Sẽ rất hay nếu có thể tạo một autoencoder học cách nhận mã Python hoặc ngôn ngữ bớt kỳ quặc hơn rồi xuất ra Piet
    Khi đó có lẽ còn có thể tạo ra các thuật toán ngẫu nhiên kiểu như Stable Diffusion