[Learning Ruby] เกมฆ่าตัวเลข

เปิดซีรีส์ที่สองไปพร้อมกันครับกับ Learning Ruby บันทึกประสบการณ์การเรียน Ruby ของผม

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

ที่เรียนรูบี้เนี่ยเพราะพ่อแนะนำมาครับ พ่อนั่งดูสมัครงานหลายที่แล้วก็พบว่ามันมีแต่ Ruby. Python มีน้อยมาก ผมก็อะเรียน Ruby ก็ได้

ย้อนกลับไปสมัยขึ้นม. 2 ตอนนั้นที่เลือก Python คือผมเล็งเรียนภาษาใหม่อยู่ครับ ตอนนั้นมี Python (Django) กับ Ruby (on Rails) ที่ผมกำลังตัดสินใจเลือก ผมเลือก Python ด้วยเหตุผลนึงคือพ่อเคยซื้อหนังสือมา (แต่พ่อผมเขียนเป็นแต่ VB นะครับ) แล้วก็อีกเหตุผลนึงคือการบังคับ indentation ของ Python ผมรู้สึกว่ามันเป็นอะไรที่เป็นนวัตกรรมมาก และจะได้แก้โรคเขียนโค๊ดยุบรวมบรรทัดเดียวของผมไปด้วย (เคยขุดโค้ดสมัยม. 1 ใน PHP มาอ่าน ผมยังอ่านแล้วไม่เข้าใจเลย)

ผมจำจากประสบการณ์ Python ว่าตอนนั้นผมจับ web.py ก่อน เป็น framework เล็กๆ ความสามารถหลายตัว แต่ผมใช้มันเป็นแค่ URL Router เฉยๆ เขียนไปหลายงาน รวมถึงงานลูกค้าด้วย จนกระทั่งม. 4 ผมถึงลอง Django จากงานที่ครูอยากให้ทำคือทำ[โปรแกรมข้อสอบ](https://github.com/whs/Quiz) ตอนหัด Python ผมเคยลองไต่ Django แล้วสไลด์ตกกลับมาแบบงงๆ มันดูเวทย์มนต์เกินไป ฉะนั้นใน Ruby ผมจะไม่พยายามไล่ Rails แต่ให้เข้าใจภาษามันก่อน

ทีนี้ผมเริ่มเรียนจากอ่านหนังสือครับ ปกติใครถามผมจะไม่ค่อยแนะนำหนังสือโปรแกรมมิ่งให้อ่านเพราะพอเป็นแล้วไปอ่านมันจะรู้สึกว่าหนังสือเขียนไม่ดี แต่พอมาเรียนเองแล้วก็พบว่าหนังสือนี่จำเป็น แต่อ่านแค่ให้เข้าใจโครงสร้างของมันพอ แล้วเปิด docs ประกอบเวลาอยากจะทำอะไร ถ้าให้เทียบเหมือนเรียนภาษาคงเหมือนเรียน grammar มันก่อนว่า x no y คือ x ของ y แล้วผมอยากบอกว่ากระเป๋าของคุณสมชาย ผมก็เปิดดิกท์เจอว่ากระเป๋าคือ kaban ก็ kaban no somchai-san จบ

เล่มที่ผมดูผ่านๆ ก็มีหลายเล่มครับ มี [Ruby Koans](http://rubykoans.com) อันนึงที่ทำไปเล่นๆ พอตอบคำถามได้ แต่ก็ยังไม่รู้สึกว่าคืบหน้า พ่อแนะนำให้หาหนังสือ ผมก็เลยค้นๆ ดูหลายเล่มจนกระทั่งไปเจอ [Beginning Ruby](http://www.apress.com/9781590597668)

ครั้นจะไปซื้อมาเรียนมันธรรมดาไปครับ เราต้องยืนอ่านหน้าร้านก่อนสิว่าหนังสือมันดีแค่ไหน มันก็มี Google Books ให้กดดูได้ครับจากในลิงก์เมื่อกี้นี้

ด้วยความใจดีของ Google ครับ บทที่ 3 ทั้งบทอ่านได้ฟรี บทที่ 4 ก็ด้วย ผมอ่านสองบทนี้จบปุ๊บผมก็บรรลุ syntax Ruby แล้ว (ไม่เห็นต้องซื้อ ;p)

คำถามต่อมาผมก็ถามว่า เรียนแล้วไง จะเขียนอะไร พ่อก็แนะนำให้ลองเอางานลูกค้ามา reimplement แต่ผมบอกแล้วว่าผมจะไม่ไต่ rails จนกว่าจะเข้าใจ ruby พ่อเลยแนะนำว่าเขียนเกมฆ่าตัวเลขดู

เกมฆ่าตัวเลข ตอนแรกผมเข้าใจว่าจะมีตัวเลขวิ่งมาให้กด counter ให้เป็นเลขนั้นแล้วกด ok เพื่อลบออกไปก่อนที่เลขนั้นจะชนขอบจอ หลังๆ เลขจะมีหลายตัวพร้อมกัน ไอ้นี่ผมก็ไม่แน่ใจว่าเขียนยังไง แต่พอถามไปถามมาพ่อบอกว่ามันคือเกมที่จะมีตัวเลขออกมา 1 ตัว ให้เราปรับ counter เป็นเลขตัวนั้น แล้วเลขตัวนั้นจะหายไป แต่ทุกๆ เวลานึงเลขจะเพิ่มขึ้นทีละตัว เกมก็จะยากขึ้น (โดยที่ถ้ามีเลข 7 อยู่ 3 ตัวบนกระดาน กด counter เป็นเลข 7 แล้วกด enter ปุ๊บเลข 7 ทั้ง 3 ตัวจะหายไปจากเกมทันที)

ทีนี้ความยากเริ่มต้นอยู่ที่การวาด UI ครับ ผมเคยเขียนเกม​ Skifree clone บน Java ทีนึงตอนหัด Java ก็เลยได้ผ่านตากับ curses​ ซึ่งเป็นไลบรารีในการวาดจอ ผมก็เลยลองมาดูบ้างว่า curses ใน ruby มีมั้ย ปรากฎว่ามันมีครับอยู่ในตัวเลย ชื่อมอดูล [Curses](http://www.ruby-doc.org/stdlib-1.9.3/libdoc/curses/rdoc/Curses.html) ผมเห็นวิธีการใช้ใน docs คือ

~~~~
require ‘curses’
include Curses
~~~~

โอเค ไม่ยาก ผมศึกษาจาก example ใน docs สักพักก็พอเข้าใจว่า เวลา include อะไรเข้าไปแล้วมันจะไปรวมกับ global namespace สามารถเรียกใช้ได้เลย ไม่ต้องสั่ง `Curses.บลาๆ` ทุกบรรทัด สักพักก็ได้มาประมาณนี้ครับ

ผมใช้ Tick ประมวลผล ซึ่งอันนี้ผมเอาไอเดียมาจากสมัยเขียน Skifree คือผมสามารถปรับความเร็วเกมได้โดยการปรับ tick speed โดยตรง โดย tick คือความเร็วการประมวลผลของเกม (เหมือนสัญญาณนาฬิกา อะไรทำนองนั้นครับ)

สิ่งที่ผมเรียนจากโปรแกรมนี้คือ

– การใช้ curses
– ใส่ $ นำหน้าตัวแปร คือ global variable, ชื่อตัวแปรตัวแรกตัวใหญ่คือค่าคงที่ ชื่อคลาสทั้งหมดต้องเป็นค่าคงที่ ฉะนั้นคลาสจะต้องตัวแรกตัวใหญ่เสมอ
– keycode ของปุ่มขึ้น/ลง ไม่ใช่ตัวเลขตัวเดียว ใช้ `Curses.KEY_UP` ไม่ได้ เอาจริงๆ แล้วมันจะเป็น string `\e[A` (สามตัว) ฉะนั้นต้องดักโค้ดให้หมด input ก่อนแล้วค่อยประมวลผลว่ามันคืออะไร
– join ruby สวยมาก คือ `str.join “joiner”` ได้เลย ใน python มันใช้ `”joiner”.join(str)` โคตรน่าเกลียด
– `Array#select` คือคำสั่งแนวๆ filter (ในชุด map reduce filter นั่นแหละครับ) โดยใส่ฟังก์ชั่นด้วยบล็อค do หรือ { .. } ก็ได้ (จริงๆ เรื่องบล็อคในเล่มตะกี้มีเขียนอธิบายละเอียดแล้วครับ)
– ฟังก์ชั่นบางตัวที่มี = ต่อท้าย จะเป็น setter การใช้ต้องใช้รูปเต็มเท่านั้นคือ `Curses.timeout= 5` วรรคแบบอื่นจากนี้ไม่ได้ (ผมคิดว่าข้อมูลนี้ผิดนะ แต่จากการทดลองผมได้ประมาณนี้)
– วัดความยาว array ก็ `Array#length` เลย ไม่ต้อง `len()` ให้งง
– จะลบจาก array ก็ `Array#delete` จะลบสิ่งที่กำหนดออกจาก array ทั้งหมดไม่ว่าจะปรากฎกี่ครั้ง
– เวลาวาดจอใน Curses แล้วให้สั่ง `refresh` จะ flush buffer

ก็ เกมฆ่าตัวเลขก็ประมาณนี้ครับ โพสต์ต่อไปมากับเกมที่ยากขึ้นกว่านี้หน่อย รอดูต่อไปนะครับ!