- เป็นเกม 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 ความคิดเห็น
ความคิดเห็นบน Hacker News
ลองแยกไฟล์รันบนลินุกซ์ออกมา แล้วแปลกใจที่ readelf กับ objdump อ่านได้ไม่ปกติ
พอตรวจดูพบว่าเอา ฟิลด์ที่ไม่ได้ใช้งานของ PT_DYNAMIC header มายัดชื่อ dynamic linker ลงไปแบบฝืน ๆ เพื่อประหยัดพื้นที่
แต่คิดว่าการจงใจทำให้ฟอร์แมตพังแบบนี้เป็นการ ประหยัดที่แทบไม่มีความหมาย
มันอาจชนกับฟีเจอร์ความปลอดภัยอย่างแอนติไวรัสหรือ DEP ได้ด้วย เลยไม่อยากไปยุ่งกับไฟล์รันลักษณะนี้
รู้สึกทึ่งที่ขนาดไฟล์ของเกมต้นฉบับ Zelda เล็กมาก
น่าอัศจรรย์ที่โค้ดกับข้อมูลเพียงเท่านั้นสามารถสร้าง ความประทับใจและความดื่มด่ำ ได้มากขนาดนั้น
The Legend of Zelda (Wikipedia)
ผ่านไป 40 ปีถึงได้แยกแผนที่ออกมา ซึ่งแค่ PNG อย่างเดียวก็เกิน 800KB แล้ว
ดูแผนที่เกม
นักออกแบบจัดวางมันเหมือนปริศนาภายใต้ข้อจำกัดด้านพื้นที่
ดูโครงสร้างแผนที่ Zelda
ผลการทดสอบของฉัน
.htmlแล้วรันได้lzmaพอติดตั้งแพ็กเกจxzก็ใช้งานได้.comหรือ.exeแล้วเจอ error (0xc0000005) แต่พอ ปิดการตั้งค่า DEP ก็รันได้chmod +x snake.comแล้วรัน Mono จะพยายามเปิดและล้มเหลวแต่ถ้าใช้
bash snake.comจะทำงานได้ดี ทดสอบบน Debian 13จุดที่น่าสนใจคือไฟล์นี้เป็น ไฟล์รันสามตัวที่ถูกรวมเป็นไฟล์เดียว
เพราะงั้นในแต่ละแพลตฟอร์มก็สามารถบรรจุโปรแกรมคนละตัวกันไปเลยก็ได้
ทำให้นึกถึงเกม FPS ขนาด 96KB ชื่อ kkrieger
ตอนนั้นระดับกราฟิกของมันน่าทึ่งมาก
kkrieger (archive)
สงสัยว่าบน 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://นั้นทรงพลังมาก แต่คนมักมองข้ามพูดอีกอย่างคือไม่ได้แค่เปิด browser instance ขึ้นมา แต่ทำ native app เต็มรูปแบบมาสองตัวเลย
น่าแปลกใจที่ ไฟล์ Polyglot เพิ่งมาโผล่ช้าขนาดนี้
ในเชิงเทคนิคแล้วมันน่าจะทำได้ตั้งแต่ 10~20 ปีก่อน
นึกถึง EICAR.COM เป็นตัวอย่างยุคแรก ๆ ที่เป็นได้ทั้งข้อความและไฟล์รัน
ทำให้นึกถึงการแข่งขันเกมเว็บจิ๋วอย่าง js13kGames