บทความนี้เป็นเก็บตกตัวอย่างเกมโอเอ็กซ์หรือ Tic-Tac-Toe ที่ทางทีมเราใช้ในการสอนวิชาพัฒนาเกมด้วยภาษาต่าง ๆ ตามความเหมาะสมกับกลุ่มผู้เรียน แต่ส่วนใหญ่จะใช้ภาษาไพธอนในการนำสอนเนื่องจากอธิบายและเขียนไปด้วยได้สะดวกกว่าภาษาอื่น ประกอบกับอยากให้มองเห็นแนวทางการนำไปใช้กับแพลตฟอร์มอื่น ๆ บ้าง ทางพวกเราจึงนำตัวอย่างมาใช้กับ MicroPython ของบอร์ดที่เราตั้งชื่อกันว่า ml4m ซึ่งมีที่มาจากบอร์ดนี้ติดตั้ง TensorFlow Lite บน ESP32 แบบ ROM 4MB โดยบอร์ดมีหน้าตาดังภาพที่ 1
จากภาพที่ 1 จะพบวา บอร์ด ml4m ประกอบด้วยส่วนนำเข้าข้อมูลทั้งที่เป็น JoyStick สวิตช์แบบสัมผัส สวิตช์แบบกด (เพาะสวิตช์ของจอยสติกเสีย ……. เหอะ ๆ) มีเซ็นเซอร์ BME280 (ที่ส่วนของความชื้นเสีย) และ MPU6050 สำหรับใช้อ่านค่าการเคลื่อนที่ของแกนต่าง ๆ พร้อมทั้งมีส่วนนำออก 2 ชิ้น คือ ลำโพง และ OLED ซึ่งการเชื่อมต่อกับ OLED ใช้ SCL ของ OLED ต่อเข้ากับขา GPIO22 และ SDA ของ OLED เข้ากับขา GPIO21 ดังภาพที่ 2
การเชื่อมต่อของสวิตช์ทั้ง 4 คือ swA, swB, swC และ swD ต่อเข้ากับ GPIO32, GPIO33, GPIO34 และ GPIO35 ตามลำดับ ตามภาพที่ 3
เกม Tic-Tac-Toe
เกม Tic-Tac-Toe เป็นเกมกระดานสำหรับผู้เล่น 2 คน โดยลักษณะของเกม เป็นดังนี้
1. ตาราง ขนาด 3×3
| |
---+---+---
| |
---+---+---
| |
- เล่น 2 คน
ผู้เล่น O
อีกฝั่ง X - การแพ้ชนะ ชนะ
1. เรียงกันเป็นแถวแนวนอน- เรียงกันเป็นแนวตั้ง
- เรียงกันในแนวทแยง
การเก็บข้อมูล
เกม OX ตาราง 3×3 ดังนั้น ข้อมูลต้องเป็นตาราง 3×3 โดยการเก็บตาราง ในภาษาไพธอนนั้นสามารถใช้ตัวแแปรประเภท list หรือ tuple ในการจัดเก็บ ตัวอย่างเป็นดังนี้ ซึ่งจะพบว่าตัวแปร table เป็นลิสต์ของ 3 สมาชิก โดยแต่ละสมาชิกมี 3 สมาชิกอยู่ภายใน แทนคอลัมน์แต่ละคอลัมน์ของแต่ละแถว พร้อมทั้งมีฟังก์ชัน showTable() เป็นการสั่งแสดงผลข้อมูลในตัวแปร table
# 1. ข้อมูล
table = [ # สร้างตัวแปรประเภท list
[1,2,3], # ลิสต์แถวที่ 0
[4,5,6], # ลิสต์แถวที่ 1
[7,8,9] # ลิสต์แถวที่ 2
# แต่ละแถวมี 3 คอลัมน์ : 0, 1, 2
]
# แสดงตาราง
def showTable():
print("{}|{}|{}".format(table[0][0],table[0][1],table[0][2]));
print("{}|{}|{}".format(table[1][0],table[1][1],table[1][2]));
print("{}|{}|{}".format(table[2][0],table[2][1],table[2][2]));
# โปรแกรมหลัก
showTable()
การับข้อมูลการเดินจากผู้เล่น
การให้ผู้เล่นเลือกจุดวาง O ในตารางใช้คำสั่งนำเข้า input() ซึ่งผู้อ่านต้องทดลองการทำงานโดยพิมพ์ข้อมูลในหน่างคอนโซล โดยโค้ดโปรแกรมเป็นดังนี้
# 1. ข้อมูล
table = [ # สร้างตัวแปรประเภท list
[1,2,3], # ลิสต์แถวที่ 0
[4,5,6], # ลิสต์แถวที่ 1
[7,8,9] # ลิสต์แถวที่ 2
# แต่ละแถวมี 3 คอลัมน์ : 0, 1, 2
]
# แสดงตาราง
def showTable():
print("{}|{}|{}".format(table[0][0],table[0][1],table[0][2]))
print("{}|{}|{}".format(table[1][0],table[1][1],table[1][2]))
print("{}|{}|{}".format(table[2][0],table[2][1],table[2][2]))
# รับค่าเพื่อเลือกตำแหน่งของ o, 1..9 เลือกตำแหน่ง .. 20 นาที
def getInput(who):
while True: # by Mr. Kanokpon Sawasdee1
showTable()
try:
position = int(input("choose(1...9)?"))
if((position in table[0])or(position in table[1])or
(position in table[2])):
break
else:
print("1...9only")
continue
except:
print("1...9only")
r = 0
if (position in table[0]):
r = 0
elif (position in table[1]):
r = 1
else:
r = 2
c = table[r].index(position)
table[r][c] = who
# โปรแกรมหลัก
for i in range(4):
getInput('o')
print("------------------")
getInput('x')
print("------------------")
getInput('o')
showTable()
จากโค๊ดจะพบว่ามีการตรวจสอบประเภทของข้อมูลนำเข้าว่าโดยใช้ try และ except เป็นตัวดกความผิดพลาด เพื่อให้ได้เลข 1 ถึง 9 อันเป้นตัวแทนของช่องหมายเลข 1 ถึง 9 ในตารางเกม หลังจากนั้นตรวจสอบอีกครั้งว่าตำแหน่งที่เลือกยังไม่เคยถูกเลือกมาก่อน ถ้ามีการเลือกมาก่อนหน้านี้จะรายงานความผิดพลาดและให้เลือกใหม่ แต่ถ้าไม่เคยถูกเลือกจะเปลี่ยนแปลงค่าเป็น “O”
ตรวจสอบการแพ้ชนะ
การตรวจสอบการแพ้ชนะของเกมมีด้วยกัน 8 กรณี คือ
- แถวที่ 1 เป็น O หรือ X ทั้งหมด
- แถวที่ 2 เป็น O หรือ X ทั้งหมด
- แถวที่ 3 เป็น O หรือ X ทั้งหมด
- คอลัมน์ที่ 1 เป็น O หรือ X ทั้งหมด
- คอลัมน์ที่ 2 เป็น O หรือ X ทั้งหมด
- คอลัมน์ที่ 3 เป็น O หรือ X ทั้งหมด
- ตำแหน่ง 1,5 และ 9 เป็น O หรือ X ทั้งหมด
- ตำแหน่ง 3, 5 และ 7 เป้น O หรือ X ทั้งหมด
ตัวอย่างโค้ดการตรวจสอบของกรณี แถวแรกเป็น O ทั้งหมด เป็นดังนี้
# ตรวจสอบการชนะของ O
def checkWinO():
# กรณี 1 [0] --> 'o','o','o'
if ((table[0][0] == 'o') and
(table[0][1] == 'o') and
(table[0][2] == 'o')):
print("O Win!")
return True
return False
ตาเดินของคอมพิวเตอร์
เพื่อให้การเล่นคนเดียวสะดวกขึ้นเราจึงเขียนโค้ดให้คอมพิวเตอร์เป็นผู้เล่นที่ 2 โดยหลักการทำงานของโปรแกรมฝั่งคอมพิวเตอร์นั้นเป็นดังนี้
- สุ่มเลข 1 ถึง 9 หรือ 0 ถึง 8
- ตรวจสอบว่ามีข้อมูลการเลือกมาก่อนหรือไม่
- ถ้ามีให้ทำ 1 อีกครั้ง
- ถ้าไม่มีให้เปลี่ยนค่าตำแหน่งนั้นเป็น “X”
ตัวอย่างโปรแกรม
ตัวอย่างโปรแกรมต่อไปนี้ใช้การแสดงผลไปยังจอ OLED ดังภาพที่ 4 และเมื่อมีการเลือกตำแหน่งสลับกันไปกับคอมพิวเตอร์ดังภาพที่ 5 จะมีผลลัพธ์ดังตัวอย่างภาพที่ 6
#######################################################
### Tic-Tac-Toe
#######################################################
from machine import Pin,I2C
import math
import machine
import gc
import ssd1306
import random
import time
#######################################################
## System setting
gc.enable()
gc.collect()
machine.freq(240000000)
#######################################################
## OLED
sclPin = Pin(22)
sdaPin = Pin(21)
i2c = I2C(0,scl=sclPin, sda=sdaPin, freq=400000)
oled = ssd1306.SSD1306_I2C(128,64,i2c)
oled.poweron()
oled.contrast(255)
oled.init_display()
oled.fill(0)
oled.show()
## constant
blockHeight = const(16)
blockWidth = const(16)
screenWidth = 128
screenHeight = 64
table = [1,2,3,4,5,6,7,8,9]
def drawTable():
oled.fill(0)
left = (screenWidth - (blockWidth*3))//2
top = (screenHeight - (blockHeight*3))//2
for i in range(4):
oled.hline(left-(blockWidth//4),(top-(blockHeight//4))+blockHeight*i, blockWidth*3,1)
oled.vline((left-(blockWidth//4))+(i*blockWidth),top-(blockHeight//4), blockWidth*3,1)
i = 0
for r in range(3):
for c in range(3):
oled.text(str(table[i]),left+blockWidth*c,top+blockHeight*r)
i += 1
oled.show()
def isWin(p):
win = False
# case 1 : แถวแรกเป็น p
if ((table[0]==p)and(table[1]==p)and(table[2]==p)):
win = True
# case 2 : แถวที่ 2 เป็น p
if ((table[3]==p)and(table[4]==p)and(table[5]==p)):
win = True
# case 3 : แถวที่ 3 เป็น p
if ((table[6]==p)and(table[7]==p)and(table[8]==p)):
win = True
# case 4 : คอลัมน์ 0 เป็น p
if ((table[0]==p)and(table[3]==p)and(table[6]==p)):
win = True
# case 5 : คอลัมน์ 1 เป็น p
if ((table[1]==p)and(table[4]==p)and(table[7]==p)):
win = True
# case 6 : คอลัมน์ 2 เป็น p
if ((table[2]==p)and(table[5]==p)and(table[8]==p)):
win = True
# case 7 : แนวทแยงมุมซ้ายบนลงงล่างขวา
if ((table[0]==p)and(table[4]==p)and(table[8]==p)):
win = True
# case 8 : แนวทแยงมุมจากซ้ายล่างขึ้นบนขวา
if ((table[2]==p)and(table[4]==p)and(table[6]==p)):
win = True
return win
#######################################################
### Main program
counter = 0
while True:
# วาดตาราง
drawTable()
# รับค่า
print("Human")
while True:
try:
pos = int(input("ตำแหน่งที่ต้องการเลือก(1-9)?"))
except:
print("กรุณากรอก 1..9")
if ((pos < 1) or (pos > 9)):
print("กรุ่ณากรอก 1..9")
else:
if (table[pos-1] != pos):
print("กรุณาเลือกช่องอื่น")
else:
table[pos-1] = "O"
break
drawTable()
# ตรวจสอบว่าคนชนะ ?
if (isWin("O")):
print("ขอแสดงความยินด้วยที่คุณชนะ")
break
# คอมพิวเตอร์เล่น
print("Computer:")
while True:
pos = random.getrandbits(8)%9 # สุ่ม 0..8
if (table[pos] == (pos+1)): # ถ้ายังว่าง
table[pos] = "X"
break
# ตรวจสอบว่าคอมพิวเตอร์ชนะไหม?
if (isWin("X")):
print("ขอแสดงความเสียใจกับความพ่ายแพ้ของคุณ!")
break
# นับรอบการเล่น
counter += 1
if (counter == 4):
break
drawTable()
time.sleep_ms(2000)
oled.poweroff()
สรุป
จากบทความนี้จะพบว่า เมื่อเรากำหนดเงื่อนไขการเลื่อน เงื่อนไขการคิด เงื่อนไขของการทำงาน หลังจากนั้นนำมาเขียนโค้ดจะทำให้การเขียนโค้ดนั้นทำงานได้หลากหลายระบบ ทั้งนี้เนื่องจากขั้นตอนการทำงานยังคงมีความคล้ายเดิม แต่เมื่อเปลี่ยนอุปกรณ์หรือแพลตฟอร์มสิ่งที่ต้องเพิ่มเข้ามาคือ ส่วนของการเขียนโค้ดเพื่อให้ใช้ได้กับแพลตฟอร์มนั้น อันได้แก่ วิธีการรำเข้าข้อมูลที่แตกต่างกัน และส่วนของการแสดงผล เช่นเดียวกับตัวอย่างเกม tic-tac-toe ที่ยกมาเป็นบทความในครั้งนี้ จะพบว่าหลักการเขียนเป็นภาษาไพธอน แต่เมื่อต้องการย้ายการแสดงผลไปที่ OLED จะต้องเขียนโค้ดเรื่องของการเชื่อมต่อกับ OLED พร้อมทั้งหาวิธีการเขียนโค้ดเพื่อแสดงผลให้เหมาะสมกับอุปกรณ์ เป็นต้น สุดท้ายนี้จะพบว่า การเล่นนั้นยังไม่สนุกเท่าไรนักเนื่องจากคอมพิวเตอร์สุ่มโดยไม่รู้จักป้องกัน และไม่เลือกจุดที่ดีที่สุด ดังนั้น ถ้าอยากให้เกมมีความสนุกเพิ่มขึ้น ท้าทายมากขึ้น ผู้อ่านต้องเพิ่มเติมวิธีคิดของคอมพิวเตอร์เข้าไปให้มากขึ้นกว่าการสุ่ม …. ขอให้สนุกกับการเขียนโปรแกรมครับ
(C) 2020-2021, โดย อ.ดนัย เจษฎาฐิติกุล/อ.จารุต บุศราทิจ
ปรับปรุงเมื่อ 2021-09-02, 2021-11-27