[Through the Loop] เกมกับการปฏิวัติวงการศึกษา

ผมก็ไม่รู้ Through the Loop อัพเตดมานานมากแล้วแต่บล็อคเค้าย้ายออกไปอยู่รวมกับหน้าแรกของ Strange Loop Games ก็เลยไม่ได้เอามาเขียนสักที

สำหรับตอนนี้เป็นตอนที่น่าสนใจมาก เกี่ยวกับการปฏิวัติวงการศึกษา ในบทความนี้ผมเสริมเข้าไปหลายๆ จุดจากบทความต้นฉบับด้วยนะครับ


เคยเล่น Oregon Trail ไหมครับ? คิดว่าอาจจะไม่เคยเล่นกันแต่สำหรับชาวอเมริกันแล้วเกมนี้เป็นเกมนึงที่โด่งดังมาก โดยเป็นเกมจำลองสถานการณ์ในช่วงศตวรรษที่ 19 ให้ผู้เล่นนำขบวนเกวียนอพยพไปทางตะวันตกของเมริกา ซึ่งผู้เล่นจะได้พบกับความยากลำบากต่างๆ ไม่ว่าจะเป็นการจัดสรรทรัพยากรที่มีจำกัด, ครอบครัวที่เดินทางมาด้วยกันค่อยๆ ล้มหายตายจากไปจากอุบัติเหตุและโรคระบาด และสุดท้ายผู้เล่นเองก็จะไม่รอดเหมือนกัน

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

ผู้เขียนคิดว่าความเข้าใจถึงความยากลำบากในยุคสมัยนั้นนี่แหละที่น่าจะเป็นสิ่งสำคัญที่สุดในการศึกษา มากกว่าการจำข้อเท็จจริงต่างๆ ได้ และเกมเป็นสื่อที่ดีมากๆ ในการนำเสนอประสบการณ์ให้กับผู้เล่นได้มากกว่าสื่ออื่นๆ แต่ในขณะเดียวกัน ซอฟต์แวร์ด้านการศึกษาอื่นๆ กลับไม่ได้สนใจตรงนี้เลย

“ส่งเสริมการเรียนรู้”

เวลาได้ยินเกม “ส่งเสริมการเรียนรู้” หลายคนคงนึกเบื่อหน่าย และผู้เขียนก็คิดแบบนั้น เกมเพื่อการศึกษามักมองเนื้อหาวิชาการเหมือนยาขมที่จำทนต้องกลืนไป เป็นเนื้อหาที่แยกออกไปจากตัวเกมหลัก เป็นเหมือนงานที่ต้องทำเพื่อได้มาซึ่งรางวัล เช่นว่า “แก้โจทย์เลขเหล่านี้เพื่อเล่นเกมต่อ”

ผมว่าการทำเกมแบบนี้นี่แหละให้ผลตรงกันข้ามเลย เพราะมันมีข้อความแฝงอยู่ว่า ถ้าเนื้อหาวิชาการใช้เป็นอุปสรรค์ที่จะต้องทำเพื่อเจอเนื้อหาที่สนุก เหมือนเป็นตัวร้ายแล้ว สิ่งที่เกมกำลังนำเสนอให้ผู้เล่นคือสิ่งที่ผู้เล่นควรจะต้องทำลายหรือเลี่ยง เหมือนนำเสนอว่าคณิตศาสตร์ “น่าเบื่อ แต่ก็จำเป็น”

และอีกวิธีหนึ่งก็ไม่ต่างกันเลยคือเกมที่พยายามซ่อนเนื้อหาวิชาการไว้แล้วมาเฉลยทีหลังว่า “อ้อ พัซเซิ่ลเมื่อกี้นี้คือการแก้สมการนี่เอง” สิ่งที่เกมกำลังจะสื่อคือการแก้สมการมันยากน่ารำคาญมากจนต้องหลอกให้ผู้เล่นทำตาม

ถ้าเกมเพื่อการเรียนรู้ยังอยู่ได้แค่นี้ ก็คงเป็นเกมแนวน่าเบื่อหน่ายต่อไปอีกล่ะครับ

ทำได้มากกว่านั้น

แต่แน่นอนล่ะ ใน Oregon Trail เราก็เห็นว่ามันเป็นเกมการศึกษาได้ หรือในอีกหลายๆ เกมที่เราไม่คิดว่ามันจะเป็นเกมการศึกษาได้ เช่น Portal, Braid, Minecraft

ตัวอย่างหนึ่งคือเกม Papers, Please ซึ่งให้ผู้เล่นรับบทเป็นผู้ตรวจเอกสารในยุคสงครามเย็น ผมคิดว่านี่น่าจะเป็นเกมที่ให้ประสบการณ์สำหรับยุคสงครามเย็นดีที่ดีที่สุดแล้ว

ปัญหาก็คือ การนิยามคำว่า “ส่งเสริมการเรียนรู้” โดยทั่วไปเราจะมองว่าคำว่าส่งเสริมการเรียนรู้ หมายความว่ามันจะต้องสอน ต้องเพิ่มพูดความรู้ให้กับผู้เล่น แต่ก็เพราะคิดแบบนี้นี่แหละที่ทำให้เสียโอกาสในการใช้สื่อเกมเป็นประโยชน์ในการทำสิ่งที่มันทรงพลังได้ การเร้าอารมณ์ ชวนสงสัย การให้ประสบการณ์ การนำเสนอความน่าสนใจของระบบต่างๆ ในคณิตศาสตร์ และในโลกต่างๆ ของวิทยาศาสตร์

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

แล้วเราจะทำยังไงดีล่ะ?

1. ตีความเกมเสียใหม่

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

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

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

2. การเรียนรู้เป็นเครื่องมือ ไม่ใช่จุดมุ่งหมาย

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

แทนที่จะมองว่าเนื้อหาวิชาคือศัตรูที่ต้องกำจัด (แก้สมการให้ได้ในเวลาที่กำหนด) ลองมองว่าเนื้อหาวิชาเป็นเครื่องมือ เป็นเพื่อนของเรา (ถ้าจะปลดกับดักนี้ไปได้จะต้องใช้ฝีมือทางคณิตศาสตร์) ถ้านึกดูว่าจุดมุ่งหมายของเกมไม่ใช่ให้ผู้เล่น “แก้โจทย์” แต่เป็น “เปลี่ยนเจตคติกับคณิตศาสตร์” แค่เปลี่ยนจากศัตรูไปเป็นมิตรก็ทำให้มุมมองเกมเปลี่ยนไปได้เลย

3. เรียกเข้ามา ไม่ใช่บังคับเข้าหา

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

เมื่อการเรียนรู้เป็นสิ่งที่ผู้เล่นต้องการ ไม่ใช่จุดหมายปลายทาง การได้มาซึ่งความรู้ก็จะเป็นประสบการณ์ที่แปลกออกไป

4. ระบบที่ยืดหยุ่น

จุดสำคัญของเกมคือการเล่น และการตีความคำว่าเล่นนี้ก็สำคัญ เล่น คือการทดลอง คือการฝึกฝน คือการเรียนรู้

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

หลายๆ เรื่องที่เกมการศึกษาสอนอยู่นั้นก็มีระบบที่เหมาะกับการทดลอง เช่นฟิสิกส์ คณิตศาสตร์ วิทยาศาสตร์ เหล่านี้สามารถจำลองขึ้นมาแบบ interactive ได้ ผู้เล่นสามารถทดลองปรับค่านิด ถอดระบบนี้หน่อย แล้วลองดูผลลัพท์ดูได้

โดยเฉพาะเกมคณิตศาสตร์ค่อนข้างจะขาดการนำเสนอเป็น visual ที่ชัดเจน แต่มักจะใช้การเขียนสูตร เขียนตัวเลขทั่วไป ในขณะที่ในโลกของเกมนั้นเป็นสื่อเดียวที่สามารถแสดงให้เห็นทันทีว่าการเปลี่ยนแปลงค่าเล็กน้อยเกิดผลเป็นโดมิโน่อย่างไรได้

หนทางอีกยาวไกล

เหตุผลหนึ่งที่เกมวิชาการนั้นเป็นแบบนี้ก็เนื่องจากแรงกดดันจากภาคการศึกษา การเรียนรู้ถูกตั้งเป้าหมายว่าการประเมินนักเรียนต้องออกมาดี คะแนนสอบนักเรียนต้องออกมาดี ในขณะที่การวัดอะไรนามธรรมอย่าง “เจตคติต่อคณิตศาสตร์” นั้นอาจจะมองว่าวัดไมไ่ด้ ไม่สำคัญ

มันเหมือนสุภาษิตของฝรั่งที่ว่าสอนคนให้จับปลา “ถ้าสอนนักเรียนแยกสมการ เค้าจะสอบผ่าน แต่ถ้าสอนนักเรียนว่าทำไมจะต้องสนใจคณิตศาสตร์ นี่แหละจะเป็นของขวัญอันยิ่งใหญ่”

ผู้เขียนมองว่าจุดเริ่มต้นการเปลี่ยนแปลงนี้คงไม่ได้มาจากนโยบายด้านการศึกษาหรอก แต่มันจะมาจากนักพัฒนาเกมนี่แหละที่จะสาธิตว่านี่แหละคือสิ่งที่เป็นไปได้ นี่แหละคือวิธีการวัดผลแบบใหม่ที่วัดอะไรได้มากกว่าเดิม


ผมนึกไปถึง Fallout 3 นะกับการที่เอาเกมมาเป็น “ตัวช่วย” ตัวอย่างเช่น ในเควสที่จะเข้าสู่เกมหลักตัวละครของเราจะต้องหนีออกจาก Vault 101 โดยสามารถปลดประตูทางออกได้สามทางคือ

  1. ข่มขู่ Overseer
  2. ล้วงกระเป๋า Overseer
  3. ค้นตู้
  4. ฆ่า Overseer (ซึ่งดันเป็นพ่อของเพื่อนสนิทเรา)
  5. Hack terminal

ฉะนั้นแล้วผู้เล่นสามารถเลือกที่จะไม่ Hack terminal ได้อยู่หลายวิธี แต่ถ้าจะเป็นคนดีจริงๆ Hack terminal น่าจะเป็นวิธีที่ดีที่สุดแล้ว การ hack terminal นี่ก็เหมือนเกม mastermind ครับ คือจะมีรายการคำมั่วๆ อยู่หลายคำบนจอที่ความยาวเท่ากัน ให้เราเลือกคำมาจะบอกว่าถูกกี่ตัว (ต้องถูกทั้งตัวและตำแหน่ง) สามารถลองได้ 5 ครั้งถึงจะถูกล็อค (หรือวิธีทั่วไปที่เล่นคือลอง 4 ครั้งแล้วกดออกเข้าใหม่ซึ่งเกมจะสุ่มเฉลยใหม่ด้วย)

อีกเกมนึงที่ผมนึกออกคือ SpaceChem เป็นเกมพัซเซิลซึ่งผมว่าปัญหาของมันคือ appeal ค่อนข้างต่ำ แต่ผมว่ามันอธิบายพันธะเคมี เธรดและ synchronization ได้ค่อนข้างดีเลยและไม่บังคับให้ผู้เล่นต้องเรียนด้วย โดยตัวเกมนั้นจะให้โมเลกุลมาเป็น input 2 อัน (อาจจะเป็นธาตุเดี่ยวๆ หรือโมเลกุลแล้วแต่ด่าน) ซึ่งตรงนี้ตัวเกมก็จะมีบอกชื่อให้เลย เช่นว่านี่คือ NH3 แอมโมเนีย เสร็จแล้วก็จะต้องเอาไปเปลี่ยนเป็นพันธะใหม่ตามที่ด่านกำหนดไว้

ทีนี้อย่างแรกเลยคือเธรดครับ เกมจะมี runner อยู่สองสีคือแดงกับฟ้าซึ่งจะรันคนละคำสั่งกันได้ ด่านแรกๆ อาจจะใช้สีเดียวผ่าน แต่ถ้าอยากติด high score ก็ต้องใช้สองสี และด่านหลังๆ ก็น่าจะจำเป็นต้องใช้ ตรงนี้ก็จะทำให้เราเข้าใจถึงการออกแบบการประมวลผลแบบคู่ขนาน

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

พวกนี้นี่คอนเซปต์เรื่อง threading ทั้งนั้นเลยนะครับเนี่ย

อีกอันนึงซึ่งผมเองก็ได้จากในนี้คือเรื่องพันธะครับ มันจะมีด่านนึงที่ผมไม่แน่ใจว่าผ่านแบบอื่นได้มั้ย แต่วิธีที่ผมผ่านและผมต้องไปนั่งทำความเข้าใจเองมาคือการใช้ลิมิตพันธะให้เป็นประโยชน์ (ตัวเกมมีพูดถึงอยู่ครับ แต่ไม่มีบอกในด่านเลยว่าด่านนี้ต้องใช้) คือตัวเกมจะมีตารางธาตุให้เลยเพื่อดูว่าธาตุนี้มีพันธะได้ไม่เกินเท่าไร (และเช่นกันเกมไม่เคยบอกผมว่าต้องกดดูตารางธาตุ แต่มันก็มีสอนวิธีดูจำนวนพันธะถ้าอยากดู) สิ่งที่ผมใช้คือถ้าเกมต้องการพันธะ 2 กับ 3 ผมสามารถโยนโมเลกุลเข้าไปแล้วสั่ง +3 พันธะได้เลยเพราะว่าธาตุที่ต้องการ 2 พันธะนั้นมันมีพันธะได้ไม่เกิน 2 อยู่แล้ว (อีก +1 ก็จะไม่มีอะไรเกิดขึ้น) แล้วผมก็เอามันหลบออกไปเอาโมเลกุลอีกคู่ใส่เข้าไปแล้ววนกลับไปที่ชุด +3 เมื่อกี้เป็นอันว่าได้พันธะตรงตามโจทย์

ตอนค้นพบนี่มันเป็นโมเมนต์ที่ยูเรก้ามากครับ และนั่นก็หลายปีแล้วแต่ผมยังจำสมบัติตัวนี้ได้อยู่เลย

Online Judge (grader) design

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

ในภาคซอฟต์แวร์จะมีกิจกรรมอยู่อันนึงครับคือ SOSCamp เป็นการติวโปรแกรมมิ่งให้น้องใหม่ซึ่งก็จะมีการทำโจทย์ พอทำโจทย์ก็ต้องมีระบบตรวจ เพื่อนรีเควสมานานมากกกแล้วว่าอยากรู้ design ของระบบก็เลยเขียนสักหน่อย

## Overall

ระบบแบ่งเป็น 3 ส่วนครับคือ UI, API, Grader ซึ่งผมเลือกเป็น PHP หมดเลยเพราะช่วงนั้นเห่อ [Laravel](http://laravel.io) และ [Silex](http://silex.sensiolabs.org) นิดๆ และถ้ามีคนจะช่วยทำ ใช้ Silex บน PHP น่าจะง่ายกว่าใช้ Django โดยเฉพาะคนที่เขียน Java มา คือ Silex ค่อนข้างจะ low level มาก เขียนอะไรๆ มันจะตรงไปตรงมาไม่มีลัดเท่าไร แล้วก็ PHP Class มันหน้าตาเกือบ Java ตัด type

## UI

ตัว UI เขียนด้วย AngularJS ทั้งหมดเลยเพราะผมอยากลองเล่น [AngularJS](http://angularjs.org) พอดี เขียนเสร็จก็เรียนรู้มันไปเยอะแต่รู้สึกว่ายากอยู่ ตอนหลังมารื้อทำใหม่แล้วเอาระบบ resolve ออกพบว่าเขียนง่ายขึ้นเป็นกอง

ด้วยสไตล์ของ Angular อยู่แล้ว ตัว UI ทั้งหมดออกแบบเป็นแบบ single page app คือมี HTML file เดียวรันทุกอย่าง ไม่มีการเปลี่ยนหน้าใดๆ นอกจากการใช้ AJAX ดึงข้อมูลมาแสดง ตรงนี้การเลือกใช้ Angular + Angular-ui-router ทำให้เขียนง่ายและดีมากๆ

ข้อดีอย่างนึงของการทำ Single page app คือมันบังคับให้ทุกอย่างเป็น API พอจะทำใหม่ก็ทำได้ง่ายๆ แค่เขียนตัวครอบ API ขึ้นมาใหม่ จบ

## API

ฝั่ง API ใช้ Silex ครับ ผมลองเขียนแบบ OOP ดูโดยทำคลาสที่สามารถ extend มันมาแล้วระบุชื่อ Eloquent model และ URL (Eloquent เป็น library สำหรับต่อ database ของ Laravel ซึ่งผมดึงมาใช้เดี่ยวๆ ไม่เอา Laravel มาทั้งตัว เป็น ORM ตัวเดียวใน PHP ที่ผมชอบ) จะได้เป็น API ที่สามารถ List, read, write ลง database ได้ทันทีพร้อมระบบ access control ซึ่งก็ลดเวลาพัฒนาไปได้มาก ตอนหลังผมแกะระบบตัวนี้ไปใช้ในระบบลงทะเบียนงานบายเนียร์ด้วย วันเดียวก็เขียนระบบเสร็จ

## Grader

ส่วนสำคัญที่สุดของโปรแกรมแน่นอนว่าต้องเป็นตัวเกรดเดอร์เอง ซึ่งตัวเกรดเดอร์นั้นใช้ไลบรารีหลายตัวช่วยในการพัฒนา (ผมคิดว่าบางตัวเป็น extra ไม่ใช้ก็ได้ แต่ใช้แล้วมันทำให้โปรแกรมเท่ขึ้น พวก Symfony/Console Symfony/Process พวกนี้)

ตัว grader จะต่อเข้ากับคิวงานกลาง ซึ่งระบบเก็บคิวงานที่ผมใช้คือโปรแกรม beanstalkd และใช้ library pheanstalk เพื่อเชื่อมต่อจาก PHP เมื่อมีการส่งงานเข้ามาใน API ก็จะโยนรายละเอียดงานเข้าไปใน beanstalk

ตัว grader ที่ต่อกับคิวงานนั้นจะมี infinite loop รับงานใหม่อยู่ เมื่อได้รับงานแล้วก็จะเข้าสู่ logic ในการตรวจ ซึ่งจะมีกระบวนการคือ

### 1. รันโค้ด generate test case

ตัวระบบ test case generator ผมอยากให้มันทรงพลัง สุ่มโจทย์ได้ และเป็นธรรมชาติที่สุด ก็เลยใช้วิธีเขียนโปรแกรมมาครอบโปรแกรมที่ใส่ไว้ในโจทย์ ข้อเสียคือรองรับการส่งได้แค่ภาษาที่มีโปรแกรมครอบ

หน้าที่ของโปรแกรมครอบไม่ยากครับ ขั้นแรกโปรแกรมจะ include โปรแกรมที่โจทย์กำหนดเข้ามา (ใน Java ใช้ reflection, Python ใช้ exec ซึ่งผมว่าไม่ดีเพราะ \__name__ มันไม่เปลี่ยน, PHP ใช้ include) ทีนี้ขั้นต่อมาจะแยกตามภาษา

– **PHP:** มันคือ `echo json_encode(run());` นั่นล่ะครับ
– **Java:** มันจะส่ง `LinkedList` เข้าไปโดย pass by reference แล้วเอา LinkedList ตัวนั้นส่งเข้า GSON ออกมา
– **Python:** ผมจำไม่ชัวร์แต่น่าจะประมาณว่าเอา for in วนเพื่อทำเป็น list แล้วโยนใส่ `json.dumps`

จริงๆ มันก็ดูไม่ช่วยเท่าไรนะ แต่ผมว่า python เนี่ยมันเขียนสนุกดีตรงที่ `yield` รัวๆ ไม่ต้องเอาข้อมูลไปพักใส่ array อะไรแล้ว return ออกมาทีเดียวเหมือนภาษาอื่นๆ (PHP ก็มี yield แต่มันใหม่เกินไป)

### 2. คอมไพล์โค้ดเฉลยและโค้ดคำตอบ

มาถึงขั้นตอนสำคัญครับคือการคอมไพล์โค้ด ซึ่งก็ไม่มีอะไรยากเท่าไรคือรัน compiler ตามแต่ภาษาแล้วให้มันออกมาที่ไฟล์ที่กำหนดก็เสร็จ แต่ความยุ่งยากอยู่ที่ Java ซึ่งชื่อคลาสจะต้องเป็นไปตามชื่อไฟล์ด้วย ผมก็เลยใช้วิธีโกงๆ คือตั้งชื่อไฟล์ว่า Input.java แล้วคอมไพล์ ถ้ามันผ่านก็จบ ไม่ผ่านตัว `javac` จะบอกมาว่า should be named ….java ก็ให้โปรแกรมมันอ่าน error ตัวนี้ไปแล้ว rename file ให้ (และเก็บชื่อคลาสไว้ด้วยเพราะตอนรันต้องใช้)

### 3. Run

ขั้นตอนการรันก็รันแบบตรงไปตรงมาครับ ทีนี้จะพูดถึงเทคนิคการ sandbox หรือป้องกันความปลอดภัยของโค้ดสักหน่อย ซึ่งจริงๆ มันทำงานมาตั้งแต่ส่วน generate test case แล้ว

ตัว grader จะใช้โปรแกรม docker ในการรันครับ ซึ่งหลักการคร่าวๆ ของ docker มันคือการใช้ความสามารถ namespace, cgroup ของ linux kernel ใหม่ๆ ช่วยแยกการทำงาน คนที่อยู่ namespace อื่นๆ จะมองไม่เห็นกันเอง แต่ข้างนอกสุดมองเห็นข้างในได้ นอกจากนี้มันยังสามารถกำหนดทรัพยากรให้ cgroup ได้อีกด้วย (คล้ายๆ virtualization แบบ virtualbox อะไรพวกนี้อะครับ แต่มัน overhead ต่ำกว่ามากเพราะรันอยู่ใน kernel ตัวเดียวกันเลย)

ข้อดีอย่างนึงของ docker คือสภาวะข้างในมันคงที่ถ้าเราก๊อปไป นั่นหมายความว่าถ้าเราเซตดีๆ version ของ compiler, runtime, library จะเหมือนกันหมดเลยถึงเราจะกระจาย grader ไปหลายๆ เครื่องก็ตาม

ทีนี้เวลารันด้วยความสามารถจำกัดทรัพยากรก็ทำให้สะดวกโยธินในการจัดการแล้วครับ

– memory limit บังคับด้วย cgroup ได้เลยตรงๆ
– networking ก็บังคับได้เลยว่าไม่ให้มีการ์ดแลน
– time limit อันนี้ใช้ symfony/process จับเวลาเอา ถ้าเกินแล้วก็สั่งให้ docker kill ทิ้ง
– privileges ก็สั่งให้ docker รันใน user ปกติ (ปกติ docker จะรันด้วย root) ก็เรียบร้อย

### 4. Cleanup

เสร็จแล้วก่อนจะจากก็ต้องลบไฟล์พวกที่เป็นผลจากการคอมไพล์ต่างๆ และตัว container ออก (ใส่เข้ามาทีหลังตอนเทส fork bomb ดู) เป็นอันเสร็จ

### 5. Submit

ตัว grader จะส่งงาน 2 รอบคือตอนที่ได้งานมา (เพื่อแสดงสถานะ grading) และเมื่อเสร็จแล้วจะส่งข้อมูลกลับเข้าไปใน API อีกครั้งหนึ่ง ซึ่งก็เป็น JSON ธรรมดา โดยอาศัยการยึนยันตัวแบบ shared secret คือ API, grader จะมี string ตัวหนึ่งเปรียบเสมือนรหัสผ่านที่ต้องส่งให้ API ทุกครั้งที่จะอัพเดตสถานะงาน

## อื่นๆ

จริงๆ แล้วไม่ใช้ docker ก็ได้นะครับ เช่น elab ใช้โปรแกรม box ถ้าผมจำไม่ผิดมันจะใช้ความสามารถ ptrace คือเมื่อโปรแกรมลูกมีการเรียกฟังก์ชั่นของระบบ โปรแกรมแม่ (นั่นคือ box) จะถูกถามว่าอนุญาตหรือไม่ก่อนที่จะทำงานได้ (chrome เก่าๆ รู้สึกว่าจะใช้เทคนิคนี้เหมือนกันบนลินุกซ์) ตอนหลัง box มีรุ่นใหม่คือ isolate ซึ่งก็ใช้ cgroup เหมือนกัน แต่ผมเดาว่าน่าจะ overhead น้อยกว่า docker

(ผมไม่ได้เช็ค fact ตรงนี้นะ ไปค้นดูละเอียดๆ เองดูก่อนถ้าจะลองทำดู)

หรือจะใช้วิธีดิบเถื่อนรันไปตรงๆ เลยก็ได้ ซึ่งผมเดาว่ารุ่นพี่คงจะใช้กันเพราะมันเขียนง่ายดี แต่อาศัยความเชื่อใจว่าจะไม่มีใครส่งอะไรน่ากลัวเข้าไปในระบบ ยิ่งบางทีไม่ได้เขียน time limit ไว้ด้วย ส่ง infinite loop เข้าไปก็เรียบร้อย

ผมคิดไว้ทีนึงว่าวิธีนึงที่ทำให้มันเร็วขึ้นคือรัน docker แช่ไว้เลยแล้วข้างในมีโปรแกรมที่เขียนในภาษานั้นๆ แล้วข้างนอกแค่บอกให้รันโค้ด ซึ่งมันจะตัด overhead เรื่องการ start, stop ตัว docker และ runtime ของภาษานั้นๆ ไปได้พอสมควรเลย แต่ข้อเสียคือเรื่อง memory leak และการ limit ต่างๆ จะทำได้ยากกว่า ใน source ของ grader จะมีคลาส EnhancedJava อยู่ซึ่งผมก็ใช้หลักการตรงนี้ แต่เขียนไม่เสร็จเพราะเขียนๆ ไปแล้วนึกถึงเรื่องการจับ limit นี่แหละแล้วคิดว่าเลิกทำดีกว่า ให้ผมคุมเองน่าจะมีปัญหา security ตามมาแหงๆ