ทบทวน Python กับไลฟ์การทำ Wordle ของน้อง Mari Taiga
พอดีมีคนแชร์ event น้องเขา แล้วน่าสนใจดี เลยมาฟังตอนสองทุ่มของวันอาทิตย์ที่ 23 มกราคม 2564 จ้า ไลฟ์นี้ 45 นาที สั้นกระชับ เข้าใจง่ายจ้า
![](https://www.mikkipastel.com/content/images/2022/01/272106631_226100196383154_4967005126625547590_n.jpeg)
ไลฟ์ย้อนหลังอยู่นี่น้าาา
.
ก่อนอื่นแนะนำน้องกันก่อนเลย น้องเขาชื่อว่ามะลิ ไทกะ เป็น Vtuber สายโปรแกรมมิ่ง ซึ่งก็ทำงานสายโปรแกรมเมอร์จริงๆ ในงานน้องเขียนภาษา Go นะ
![](https://scontent.xx.fbcdn.net/v/t1.6435-1/p200x200/201579003_108099454849896_8584872782025616623_n.jpg?_nc_cat=110&ccb=1-5&_nc_sid=dbb9e7&_nc_ohc=LG4jQn8IeQ8AX-05Cfo&_nc_ht=scontent.xx&oh=00_AT9Gh_jiRW_vtmeGQHLJTuWvSSptpHpfMT8CjwJNwGwyWw&oe=62122FE6)
ทำความเข้าใจวิธีการเล่น Wordle กันก่อน
เริ่มต้นการไลฟ์ด้วยการพาไปเล่น wordle กันก่อน เพื่อทำความเข้าใจวิธีเล่นไปพร้อมกัน โดยจะไม่เล่นที่เว็บจริงนะ เดี๋ยวเป็นการสปอย เลยไปเล่นที่เว็บนี้แทน
เงื่อนไขในเกมส์หลังจากที่เราพิมพ์คำลงไป
- สีเขียว : ตัวอักษรนี้อยู่ในตำแหน่งที่ถูกต้องแล้ว
- สีเหลือง : มีอักษรนี้อยู่ในคำ แต่ยังไม่ถูกตำแหน่งนะ
- สีเทา : ไม่มีตัวอักษรนี้
ตัวเกมส์จะให้เราเดาคำที่มีตัวอักษร 5 ตัว และสามารถเดาคำที่ถูกต้องได้เพียง 6 ครั้งเท่านั้น
ดังนั้นสิ่งที่น้องมะลิ live code ให้ดูจะมี flow ดังนี้
- random คำจากใน list ออกมา
- เริ่มเกมส์
- ให้คนเล่นเดาคำ
- ตรวจสอบว่าคำนี้มัน valid ไหม โดยมี 2 เงื่อนไขที่ต้องผ่าน คือ ความยาวตรงกัน (เนื่องจากโปรแกรมนี้จะเล่นผ่าน command line) และเป็นคำที่มีความหมายจริงๆ ไม่ได้พิมพ์มั่วอะไรงี้
- แสดง character
- ตรวจสอบว่าคนเล่นเล่นชนะหรือไม่
![](https://www.mikkipastel.com/content/images/2022/01/Screen-Shot-2022-01-23-at-20.07.41.png)
เริ่มทำการ coding กันเลย
เนื่องจากน้องมะลิทำการ coding บน Windows แต่เรา coding บน MacOS ดังนั้นจะมีบางส่วนที่แตกต่างกันไปตาม OS นะ
ดึง list ออกมา และเอา response ที่ได้มาใช้
โดยน้องมะลิเลือก list คำจากเว็บของ MIT หน้าตาจะเป็น plain text แบบนี้
![](https://www.mikkipastel.com/content/images/2022/01/Screen-Shot-2022-01-23-at-21.33.30.png)
เราจะทำการ download library ผ่าน pip
กัน สำหรับ macos สามารถใช้ command นี้ทำการ install เจ้า pip
กันก่อน
python3 -m pip
โค้ดในส่วนนี้ เราจะทำการดึงข้อมูลจากเว็บนี้ออกมา และนำส่วน response.content
มาใช้ response
ที่ได้จะเป็น byte นะ
import requests
if __name__ == "__main__":
url = "https://www.mit.edu/~ecprice/wordlist.10000"
response = requests.get(url)
print(response.content)
เนื่องจากเราใช้ library ภายนอก คือ requests
ดังนั้นเราจะต้องไป install ลงเครื่องก่อน
python3 -m pip install requests
อ่าน document ของ library นี้ได้ที่นี่
![](https://docs.python-requests.org/en/v2.2.1/_static/requests-sidebar.png)
จากนั้นลองรันโค้ดของเราดูด้วยคำสั่งนี้
python3 wordle.py
ผลที่ได้จะต้องได้แบบนี้นะ
![](https://www.mikkipastel.com/content/images/2022/01/Screen-Shot-2022-01-23-at-21.48.37.png)
ผลที่ได้คืออ่านยาก เพราะตัวอักษรติดกันไปหมด แต่เราจะเห็นว่ามี \n อยู่ในนั้นเป็นการคั่นว่ามันขึ้นบรรทัดใหม่เนอะ ดังนั้นเราจะทำการ split new line ออกไป
all_words = response.content.spltilines()
แล้วเราทำการ filter เฉพาะคำที่มีความยาว 5 ตัวอักษรเท่านั้น สามารถทำได้ 2 แบบ คือ
# แบบ for-loop แล้ว check ความยาว
words = [word for word in all_words if len(word) == 5]
# filter ว่าคำนั้นมีความยาวเท่ากับ 5, อันนี้แอบคล้าย kotlin อยู่หน่อยๆ มี lambda ด้วย
words = filter(lambda word: len(word) == 5, all_words)
ส่วนตัวเราชอบท่า filter มากกว่าเพราะคล้าย kotlin 555 ผลที่ได้จะเป็น object เนอะ แต่เดี๋ยวมาต่อให้ตอนจบแล้วกัน ตอนนี้ตามน้องเขาไปก่อน
![](https://www.mikkipastel.com/content/images/2022/01/Screen-Shot-2022-01-24-at-09.10.19.png)
ในไลฟ์น้องทะลิใช้ท่าแบบ for-loop แล้ว check ความยาว ผลที่ได้คือจะได้ตัว list ที่เป็น byte เนอะ
![](https://www.mikkipastel.com/content/images/2022/01/Screen-Shot-2022-01-23-at-21.57.08.png)
จากนั้นทำการ random คำใน list นี้ออกมาเป็นโจทย์ random.choice(words)
ซึ่งผลออกมาเป็น byte อยู่ จึงทำการ decode ด้วย UTF-8
word = random.choice(words).decode("utf-8")
ผลที่ได้จะได้คำที่เป็น String ปกติมาหล่ะ
![](https://www.mikkipastel.com/content/images/2022/01/Screen-Shot-2022-01-24-at-09.16.14.png)
แสดงผลว่า game is come on!
จบในส่วนของคำโจทย์ไปแล้ว เราจะทำการเริ่มเกมส์กันเถอะ
ก่อนอื่นตั้งตัวแปรชื่อ num_guess
โดยให้เท่ากับ 0 แล้วก็ตั้ง loop while true เพื่อให้มันลองให้เราเล่นไปเรื่อยๆก่อน เดี๋ยวค่อยมาเปลี่ยนกัน ข้างในใส่ตัว input
เพื่อให้คนเล่นพิมพ์ผ่าน command line และรับสิ่งที่คนเล่นพิมพ์มาด้วยตัวแปร guess
เพื่อเอาไปใช้ต่อ
num_guess = 0
while True:
prompt = "Guess ({remaining} remaining) : ".format(remaining = 6 - num_guess)
guess = input(prompt)
ตอนนี้เราก็ลองพิมพ์ดูการทำงานของโค้ดที่เราเขียนเพิ่มขึ้นมาแล้ว
![](https://www.mikkipastel.com/content/images/2022/01/Screen-Shot-2022-01-24-at-13.09.24.png)
จัดการ condition ต่างๆภายในเกมส์
ต่อมาสร้าง function เพื่อ check condition ว่าที่คนเล่นพิมพ์ไปนั้น มีตัวอักษร 5 ตัวอักษรหรือไม่ และคำนี้มีความหมายไหม
ก่อนอื่นมาลง library กันเพิ่มเติม ชื่อ pyenchant
python3 -m pip install pyenchant
สำหรับ macOS ไม่ได้อ่ะ ต้องลงผ่าน homebrew
brew install enchant
library pyenchant เป็น library ไว้ check เรื่อง spell นะ สามารถอ่าน document ได้ที่นี่เลย
![](https://pypi.org/static/images/twitter.6fecba6f.jpg)
จากนั้นสร้าง function ที่มีชื่อว่า is_valid()
ขึ้นมา เพื่อ check ความยาวของคำ และเรื่อง wording spell ตัว enchant จะใช้เป็นของภาษา en_US
เนอะ
ถ้าคำที่คนเล่นพิมพ์มีความยาวเท่ากับ 5 และเป็นคำที่ wording spell ถูกต้อง ให้ return True
ที่เหลือก็ return False
ไป
import enchant
enchant_dict = enchant.Dict("en_US")
def is_valid(guess, word):
if len(guess) != len(word):
print("wrong length")
return False
if not enchat_dict.check(guess):
print("not a valid guess")
return False
return True
แล้วก็เอาไปใช้ในโค้ดหลักแบบนี้
num_guess = 0
while True:
prompt = "Guess ({remaining} remaining) : ".format(remaining = 6 - num_guess)
guess = input(prompt)
if not is_valid(guess, word):
print ("invalid guess")
continue
ลองรันจริงจะประมาณนี้เนอะ
![](https://www.mikkipastel.com/content/images/2022/01/Screen-Shot-2022-01-24-at-13.48.47.png)
จากนั้นมาทำการแสดงสีของตัวอักษร เป็น class ของสี โดยทางเราแอบตกใจกับการใส่ค่าสีของน้องมาก น้องใช้ ASCII color codes ปกติเราใช้แต่ hex color หน่ะสิ
![](https://www.mikkipastel.com/content/images/2022/01/Screen-Shot-2022-01-23-at-20.26.56.png)
สีหลักๆที่ใช้คือสีเขียว สีเหลือง และสีปกติ
จากนั้นสร้าง display_characters()
ขึ้นมา เพื่อแสดงสีตาม condition ของเกมส์
def display_characters(guess, word):
for i in range(len(guess)):
if word[i] == guess[i]:
print(colors.GREEN + guess[i], end='')
elif guess[i] in word:
print(colors.YELLOW + guess[i], end='')
else:
print(colors.NORMAL + guess[i], end='')
print(colors.NORMAL)
การทำงานก็ประมาณนี้แหละ แต่หลายๆคนก็ไม่มีใน enchant อะเนอะ
![](https://www.mikkipastel.com/content/images/2022/01/Screen-Shot-2022-01-24-at-15.35.22.png)
แต่ยัง 6 remaining อยู่เลย ดังนั้นเราจะมาเพิ่มค่า num_guess
ทีละ 1 กัน
num_guess = 0
while True:
prompt = "Guess ({remaining} remaining) : ".format(remaining = 6 - num_guess)
guess = input(prompt)
if not is_valid(guess, word):
print ("invalid guess")
continue
num_guess += 1
display_characters(guess, word)
![](https://www.mikkipastel.com/content/images/2022/01/Screen-Shot-2022-01-24-at-15.51.48.png)
แต่เท่าที่เราลองเล่นไปเนี่ย ก็ไม่รู้ว่าคำที่เราใส่ถูกไหมหน่ะสิ แล้วถ้าครบ 6 แล้วจะยังไงต่อ เลยมาทำ check_win()
กันต่อ ถ้าเราเดาคำถูก หรือจำนวนโควต้าในการเดาหมดแล้ว ให้ return true
ถ้ายังเราไม่ถูกแล้วยังเหลือโควต้า ให้ return false
ไป
def check_win(guess, word, num_guess):
if guess == word:
print("You win!! Congrats")
return True
elif num_guess == 6:
print("Out of guesses :(")
print("Answer was: ", word)
return True
else:
print("Incorrect. Try again")
return False
แต่ naming ชื่อแอบแปลกๆ เพราะตอนเราเรียกใช้ เราอยากรู้ว่าเกมส์จบยังอ่ะ อาจจะเปลี่ยนชื่อเป็นแบบนี้ดีกว่า
def check_end_game(guess, word, num_guess):
if guess == word:
print("You win!! Congrats")
return True
elif num_guess == 6:
print("Out of guesses :(")
print("Answer was: ", word)
return True
else:
print("Incorrect. Try again")
return False
กลับมาที่ function หลักของเรากัน เราจะเปลี่ยน condition การลูปจากเดิม while True
เป็น while not is_end_game
แทน แบบเกมส์ยังไม่จบก็เล่นต่อให้จบนะ
num_guess = 0
is_end_game = False
while not is_end_game:
prompt = "Guess ({remaining} remaining) : ".format(remaining = 6 - num_guess)
guess = input(prompt)
if not is_valid(guess, word):
print ("invalid guess")
continue
num_guess += 1
display_characters(guess, word)
is_end_game = check_end_game(guess, word, num_guesses)
มาลองเล่นกัน เดาถูกด้วยหล่ะ
![](https://www.mikkipastel.com/content/images/2022/01/Screen-Shot-2022-01-24-at-16.09.55.png)
ถ้าอยากเล่นต่อหล่ะ?
แน่นอนว่าเกมส์มันสนุก อยากเล่นต่อกันใช่ไหมหล่าาา~~~
แล้วเราเขียนโค้ดให้ replay ใหม่ได้ไหมนะ?
ได้สิ มาาาาาาาา!!!!!
ก่อนอื่นสร้าง prompt มาเพิ่ม เพื่อรับ input จาก user ว่าอยากเล่นต่อไหม โดยถ้าสิ่งที่คนเล่นพิมพ์เป็น Y ก็คือค่า play_again
เป็น True
ถ้าไม่ใช่ก็เป็น False
เราแอบปรับโค้ดเพิ่มจากของน้องเขานิดนึง เผื่อคนขี้เกียจพิมพ์เป็น y แทน
play_again_prompt = input("play again? Y (Yes)")
play_again = True if play_again_prompt.lower() == "y" else False
แล้วก็เพิ่ม for-loop ข้างนอกอีกชุดนึง ว่าเขาจะเล่นใหม่ไหม play_again
ถ้าใช่ก็เล่นใหม่ ค่า default จะเป็น True
เนอะ
num_guess = 0
play_again = True
is_end_game = False
while play_again:
while not is_end_game:
prompt = "Guess ({remaining} remaining) : ".format(remaining = 6 - num_guess)
guess = input(prompt)
if not is_valid(guess, word):
print ("invalid guess")
continue
num_guess += 1
display_characters(guess, word)
is_end_game = check_end_game(guess, word, num_guess)
play_again_prompt = input("play again? Y (Yes)")
play_again = True if play_again_prompt.lower() == "y" else False
อ่ะลองเล่นดู รอบแรกตอบถูกเรียบร้อย เกมส์จบก็จะขึ้นมาว่า จะเล่นอีกม่ะ
![](https://www.mikkipastel.com/content/images/2022/01/Screen-Shot-2022-01-24-at-18.58.42.png)
อยากเล่นต่อแต่ไม่ยอมให้เล่นอ่ะดิ ก็เลยต้องออก
![](https://www.mikkipastel.com/content/images/2022/01/Screen-Shot-2022-01-24-at-19.00.07.png)
สาเหตุเพราะว่าบางค่าอย่าง num_guess
กับ is_end_game
ไม่ได้ reset ใหม่ แล้วก็คำในชุดใหม่ด้วย ดังนั้นเราจะต้องย้ายโค้ดพวกนี้มาก่อน for-loop is_end_game
ก่อน
จากนั้นมาลองเล่นกันอีกที ตอนนี้เราเล่นใหม่ได้แล้วหล่ะ
![](https://www.mikkipastel.com/content/images/2022/01/Screen-Shot-2022-01-24-at-19.09.01.png)
โค้ดทั้งหมดจะเป็นแบบนี้จ้า
.
มีคนถามในไลฟ์ว่าเรื่อง response ไม่ใช่ 200 แล้วจะยังไงต่อ
เราเข้าใจน้องแหละว่าเราจะต้อง assume ก่อนว่าส่วนหน้าบ้านทำงานได้ แล้วค่อยๆทำไปทีละจุดจน logic หน้าบ้านครบ แล้วค่อยมาเก็บส่วนหลังบ้านทีหลัง ก่อนจบไลฟ์น้องก็ดูพวกนี้นะ
![](https://www.mikkipastel.com/content/images/2022/01/Screen-Shot-2022-01-23-at-20.43.09.png)
ใน github น้องเขาก็มีอัพเดตตรงนี้อยู่
จริงๆเนี่ยการที่ไม่ response 200 การจัดการการทำงานต่างๆ การวาง UI ในการทำงานจริง dev อาจจะไม่ได้เป็นคน design ทั้งหมดงี้ บางเคสก็ต้องสื่อสารให้ user เข้าใจด้วย ก็เป็นเรื่องที่แอบยุ่งยากนิดนึง
แต่ถ้าแบบไม่ response ไม่ 200 เราเองก็นึกไม่ออกว่าจะให้ทำอะไรนอกจากบอกว่าเกมส์เล่นไม่ได้หรอ อื้มมมมมม เรื่องนี้เราขอข้ามไปแล้วกันเนอะ แหะๆ
แล้วถ้าใช้อีกท่านึงหล่ะ
การที่เรา filter คำทั้งหมดจาก list ว่าเป็นคำที่มี 5 ตัวอักษรนั้น จะมี 2 ท่า คือ
# แบบ for-loop แล้ว check ความยาว
words = [word for word in all_words if len(word) == 5]
# filter ว่าคำนั้นมีความยาวเท่ากับ 5, อันนี้แอบคล้าย kotlin อยู่หน่อยๆ มี lambda ด้วย
words = filter(lambda word: len(word) == 5, all_words)
ถ้าเลือกท่า filter ตัว result ที่ได้เป็น object ดังนั้นเราจะต้องแปลง object เป็น list เพื่อให้เหมือนเพื่อนตอนท่าแรก โดยการ . . .
words = list(filter(lambda word: len(word) == 5, all_words))
ครอบ list()
เพื่อแปลง type object เป็น list แบบนี้เลย
ที่เหลือก็ทำงานได้ตามปกติหล่ะ
จบบล็อกนี้แล้วจ้า สวัสดี
กด follow Twitter เพื่อได้รับข่าวสารก่อนใคร เช่น สปอย content ใหม่ หรือสรุป content เร็วๆในนี้จ้า
สวัสดีจ้า ฝากเนื้อฝากตัวกับชาวทวิตเตอร์ด้วยน้าา
— mikkipastel (@mikkipastel) August 24, 2020
ติดตามข่าวสารและบทความใหม่ๆได้ที่
อย่าลืมกด like กด share บทความกันด้วยนะคะ :)
Posted by MikkiPastel on Sunday, 10 December 2017
Subscribe ช่อง YouTube ของเราได้ที่
download แอพอ่านบล็อกใหม่ของเราได้ที่นี่