4 คะแนน โดย GN⁺ 2026-01-13 | 1 ความคิดเห็น | แชร์ทาง WhatsApp
  • เป็นเกม Snake ที่รันได้ทั้งบน Windows, Linux และเบราว์เซอร์ด้วย ไฟล์เดี่ยวขนาด 13KiB โดยรองรับทั้งสามแพลตฟอร์มจากซอร์สเดียว
  • ใช้ กติกา Snake แบบคลาสสิก คือบังคับงูให้กินอาหารและหลบไม่ให้ชนกำแพง พร้อมมีระบบคะแนน เลเวล และโครงสร้างเขาวงกต
  • แต่ละแพลตฟอร์มถูกพัฒนาด้วย C (WinAPI/X11) และ JavaScript (HTML5 Canvas) แล้วบีบอัดและรวมให้เหลือขนาดราว 3~5KiB ต่อส่วน
  • ฝั่ง Windows ใช้ PE header แบบผิดแผกและกลไก apphelp, ฝั่ง Linux ใช้ การบีบอัด lzma และ shell dropper, ส่วนเบราว์เซอร์ใช้ ลูกเล่น HTML/CSS เพื่อให้รันได้
  • ผลลัพธ์จากการรวมทั้งสาม implementation คือไฟล์รันเดี่ยวขนาด 13,312 ไบต์ ที่แสดงให้เห็นถึง ความเป็นไปได้เชิงทดลองของโครงสร้างไฟล์รันข้ามแพลตฟอร์ม

ความพยายามแบบมัลติแพลตฟอร์มที่ได้แรงบันดาลใจจาก Cosmopolitan libc

  • Cosmopolitan libc เป็น toolkit ที่สามารถคอมไพล์ซอร์สโค้ด C ให้เป็นไบนารีเดี่ยวที่รันได้บนหลายระบบปฏิบัติการ
    • รองรับการรันแบบเนทีฟบน OS หลากหลาย เช่น Windows, Linux, BSD
  • เนื่องจากไม่รองรับ GUI และมีข้อจำกัดด้านขนาดไบนารีที่ค่อนข้างใหญ่ ผู้เขียนจึงเลือกท้าทายตัวเองด้วยการสร้าง วิดีโอเกมขนาดไม่เกิน 16KiB
    • เป้าหมายคือสร้างเกมจากซอร์สเดียวที่รันได้ทั้งบน Windows, Linux และเบราว์เซอร์

โครงสร้างและกติกาของเกม

  • เกมนี้เป็น Snake มาตรฐาน ควบคุมด้วยปุ่มลูกศรหรือ WASD
    • ออกจากเกมด้วย ESC, รีเซ็ตด้วย R, หยุดชั่วคราวด้วย P, และเริ่มเกมด้วยปุ่ม Space
  • ทุกครั้งที่กินอาหารจะได้คะแนนเพิ่ม โดย อาหารปกติได้ 10 คะแนน และ อาหารสีเหลือง (โอกาส 15%) ได้ 20 คะแนน
    • อาหารจะหายไปหลังผ่านไประยะหนึ่ง โดยเวลาที่หายจะขึ้นกับความเร็วของงู (ซึ่งแปรผันตามความยาว)
  • เมื่อ กินอาหารครบ 10 ชิ้นจะไปยังเลเวลถัดไป และตำแหน่งกำแพงจะถูกสุ่มใหม่
    • การสร้างเขาวงกตรับประกันว่าจะมีเส้นทางไปหาอาหารเสมอ
    • ตำแหน่งเริ่มต้นของงูก็ถูกสุ่มเช่นกัน แต่จะถูกวางในทิศทางที่มีพื้นที่ว่างอย่างน้อย 5 ช่อง
  • ขนาดไฟล์เกมที่เสร็จสมบูรณ์คือ 13,772 ไบต์

วิธีทำของแต่ละแพลตฟอร์ม

  • เวอร์ชัน Windows เขียนด้วย C บนพื้นฐาน WinAPI และมีทั้ง สคริปต์บีบอัดกับ decompresser stub รวมอยู่ด้วย
    • ใช้ไบต์ที่ควบคุมได้อย่างอิสระหลัง MZ signature ของ PE header เพื่อ ฝัง shell script
    • ทำให้ไฟล์เดียวกันเป็นได้ทั้งไฟล์รันของ Windows และ shell script ที่ถูกต้องบน Linux
    • ตอนรันครั้งแรกอาจขึ้นข้อผิดพลาด “The application was unable to start correctly (0xc0000005)” แต่เมื่อรันซ้ำจะทำงานได้ตามปกติ
  • เวอร์ชัน Linux ใช้การบีบอัด lzma และมี shell dropper ขนาดเล็ก สำหรับแตกและรัน ELF64 binary ที่ถูกบีบอัดไว้
    • ถูกจัดโครงสร้างให้รันโดยข้ามข้อมูลบางส่วนทั้งด้านหน้าและด้านท้ายของไฟล์
  • เวอร์ชัน HTML อาศัยพฤติกรรมของเบราว์เซอร์ที่จะมองข้ามข้อมูลส่วนต้นที่ไม่จำเป็นแล้วประมวลผล HTML
    • ใช้ CSS เพื่อซ่อนข้อมูลส่วนเกินไม่ให้ปรากฏบนหน้าจอ

การรวมเป็นไฟล์เดียวและโครงสร้างภายใน

  • ไฟล์สำหรับทั้งสามแพลตฟอร์มถูก เชื่อมต่อกันตามลำดับที่กำหนด (concatenate) เพื่อให้แต่ละสภาพแวดล้อมรันเฉพาะส่วนที่ตรงกับตัวเอง
    • Windows มองเห็น PE section, Linux มองเห็น ELF section, และเบราว์เซอร์มองเห็น HTML section
  • ขนาดไฟล์สุดท้ายคือ 13,312 ไบต์ กลายเป็น polyglot binary ที่รันได้ครบทั้งสามสภาพแวดล้อม
  • ภายในไฟล์มีทั้ง Windows PE header, โค้ดบีบอัด LZMA สำหรับ Linux และโค้ด HTML/CSS/JavaScript เรียงต่อกันอยู่
    • ในตัวอย่าง code block จะเห็น MZ signature, ตัวทำเครื่องหมาย ks, แท็ก <html> และบล็อก <script> ปรากฏตามลำดับ

นัยสำคัญทางเทคนิค

  • สาธิตโครงสร้างการรันแบบ ไฟล์เดียวที่รองรับทั้ง Windows, Linux และเบราว์เซอร์
  • ใช้ การซ้อนทับฟอร์แมต PE, ELF และ HTML เพื่อแยกเส้นทางการรันตามแพลตฟอร์ม
  • เป็นความสำเร็จเชิงทดลองที่ผสาน การบีบอัด, การ hack ฟอร์แมต, และการรันข้ามแพลตฟอร์ม ไว้ในขนาดเล็กมากเพียง 13KiB

1 ความคิดเห็น

 
GN⁺ 2026-01-13
ความคิดเห็นบน Hacker News
  • ลองแยกไฟล์รันบนลินุกซ์ออกมา แล้วแปลกใจที่ readelf กับ objdump อ่านได้ไม่ปกติ
    พอตรวจดูพบว่าเอา ฟิลด์ที่ไม่ได้ใช้งานของ PT_DYNAMIC header มายัดชื่อ dynamic linker ลงไปแบบฝืน ๆ เพื่อประหยัดพื้นที่

    1. สงสัยว่าทำแบบนี้ด้วยมือเอง หรือมีเครื่องมือที่ทำแนวนี้อยู่แล้ว
    2. แล้วก็อยากรู้ว่ามีเครื่องมือสำหรับวิเคราะห์ไบนารีผิดรูปแบบแบบนี้ไหม
    • อ่านได้ด้วย ndisasm หรือ hex editor
      แต่คิดว่าการจงใจทำให้ฟอร์แมตพังแบบนี้เป็นการ ประหยัดที่แทบไม่มีความหมาย
      มันอาจชนกับฟีเจอร์ความปลอดภัยอย่างแอนติไวรัสหรือ DEP ได้ด้วย เลยไม่อยากไปยุ่งกับไฟล์รันลักษณะนี้
    • ไม่เข้าใจว่า “choke” หมายถึงอะไร เพราะในสภาพแวดล้อมของฉันทั้งสองเครื่องมือทำงานปกติดี
  • รู้สึกทึ่งที่ขนาดไฟล์ของเกมต้นฉบับ Zelda เล็กมาก
    น่าอัศจรรย์ที่โค้ดกับข้อมูลเพียงเท่านั้นสามารถสร้าง ความประทับใจและความดื่มด่ำ ได้มากขนาดนั้น
    The Legend of Zelda (Wikipedia)

    • เหมือนเวทมนตร์จริง ๆ เกม Eindeloos บน Commodore 64 ก็ใส่แผนที่ขนาดมหึมาไว้ใน 64KB ได้
      ผ่านไป 40 ปีถึงได้แยกแผนที่ออกมา ซึ่งแค่ PNG อย่างเดียวก็เกิน 800KB แล้ว
      ดูแผนที่เกม
    • Zelda 1 มีขนาด 128KB และไม่มีการบีบอัด ภาคต่อมีขนาดราว ๆ สองเท่า
    • ดันเจียนกับถ้ำจริง ๆ แล้วถูกใส่รวมไว้ใน แผนที่สี่เหลี่ยมผืนผ้า ผืนเดียว
      นักออกแบบจัดวางมันเหมือนปริศนาภายใต้ข้อจำกัดด้านพื้นที่
      ดูโครงสร้างแผนที่ Zelda
  • ผลการทดสอบของฉัน

    • เบราว์เซอร์: เปลี่ยนนามสกุลเป็น .html แล้วรันได้
    • ลินุกซ์: ขึ้น error ว่าไม่มีคำสั่ง lzma พอติดตั้งแพ็กเกจ xz ก็ใช้งานได้
    • วินโดวส์: รันเป็น .com หรือ .exe แล้วเจอ error (0xc0000005) แต่พอ ปิดการตั้งค่า DEP ก็รันได้
    • บน Windows 11 ก็ทำงานได้ปกติ
    • ถ้าสั่งแค่ chmod +x snake.com แล้วรัน Mono จะพยายามเปิดและล้มเหลว
      แต่ถ้าใช้ bash snake.com จะทำงานได้ดี ทดสอบบน Debian 13
  • จุดที่น่าสนใจคือไฟล์นี้เป็น ไฟล์รันสามตัวที่ถูกรวมเป็นไฟล์เดียว
    เพราะงั้นในแต่ละแพลตฟอร์มก็สามารถบรรจุโปรแกรมคนละตัวกันไปเลยก็ได้

  • ทำให้นึกถึงเกม FPS ขนาด 96KB ชื่อ kkrieger
    ตอนนั้นระดับกราฟิกของมันน่าทึ่งมาก
    kkrieger (archive)

    • กระทู้ที่เกี่ยวข้อง: 2017, 2023, 2024
    • ตอนนี้ก็ยังรันบนพีซีสมัยใหม่ได้ ฉันลองดาวน์โหลดมารันเองแล้ว
    • ช่วงหลังที่เล่น Metroid Prime 4 ก็คิดขึ้นมาว่า ถ้าเอาไอเดียนี้ไปทำเกมสมัยใหม่จะออกมาเป็นแบบไหน
  • สงสัยว่าบน Mac มีวิธีรันนอกจากผ่านเบราว์เซอร์ไหม
    เพราะเจอ error cannot execute binary file

  • สงสัยว่าถ้าพัฒนาเมกานิซึมนี้ต่อ จะสามารถทำเป็น universal binary ที่ใช้งานได้จริงจังหรือเปล่า
    เช่นเขียนโค้ดด้วย Haxe แล้วคอมไพล์เป็น C++ เพื่อสร้างเวอร์ชัน Win32 กับ Linux,
    จากนั้นแปลงเป็น JavaScript เพื่อให้รันใน HTML ได้ด้วย
    แล้วค่อยรวมผลลัพธ์ทั้งสามเข้าเป็นไฟล์เดียว แบบนี้ก็ดูน่าจะทำได้

  • ชอบแนวคิดของ single executable app ที่เป็นไฟล์เดียวแล้วรันได้ทุกที่
    ฉันเองก็กำลังศึกษาทิศทางนี้อยู่ตอนทำแพลตฟอร์ม serverless
    เป็นโครงสร้างที่ทำแอปแบบ data-driven ได้ด้วยไฟล์ .html เพียงไฟล์เดียว และโหลด web-components จากระยะไกล
    ความสามารถในการเปิด HTML ตรง ๆ ผ่านโปรโตคอล file:// นั้นทรงพลังมาก แต่คนมักมองข้าม

    • ผู้เขียนต้นฉบับทำไว้จริง ๆ ถึงสามรอบ — เวอร์ชัน C บน WinAPI, เวอร์ชัน C บน X11 และเวอร์ชัน JS บน HTML5 Canvas
      พูดอีกอย่างคือไม่ได้แค่เปิด browser instance ขึ้นมา แต่ทำ native app เต็มรูปแบบมาสองตัวเลย
    • น่าเสียดายที่ในสภาพแวดล้อมที่ไม่ใช่ HTTPS เบราว์เซอร์จะมีข้อจำกัดด้านฟีเจอร์เยอะ
    • อยากรู้แนวทางการพัฒนาแอปแบบ HTML เดี่ยวให้มากกว่านี้ ถ้ามีตัวอย่างที่เปิดเผยไว้ก็อยากดู
  • น่าแปลกใจที่ ไฟล์ Polyglot เพิ่งมาโผล่ช้าขนาดนี้
    ในเชิงเทคนิคแล้วมันน่าจะทำได้ตั้งแต่ 10~20 ปีก่อน

    • สงสัยว่าไฟล์ polyglot แรกสุดปรากฏขึ้นเมื่อไร
      นึกถึง EICAR.COM เป็นตัวอย่างยุคแรก ๆ ที่เป็นได้ทั้งข้อความและไฟล์รัน
  • ทำให้นึกถึงการแข่งขันเกมเว็บจิ๋วอย่าง js13kGames