เปิดมาเกมเปล่า ๆ ที่ "รันได้เลย": ผู้เล่นขยับได้ + มี score บนจอ มี 3 ช่อง TODO ให้เติมเอง

# practise_codes/mygame.py — โครง update() เปล่าที่รอเราเติม
def update():
global score
k = game.keys() # อ่านปุ่มจากจอย
if k.left: player.move(-PLAYER_SPEED, 0)
if k.right: player.move(PLAYER_SPEED, 0)
if k.up: player.move(0, -PLAYER_SPEED)
if k.down: player.move(0, PLAYER_SPEED)
# BACK = ออกเกม, START = เริ่มใหม่ -> game.run() จัดการให้เอง
# TODO #2 gameplay: game.hit() + score
# TODO #3 polish: Box -> Sprite + game.sfx("gameover")
return True # เล่นต่อ (return False เฉพาะตอนชนะ/แพ้จริง)
โครงทั้งไฟล์มีแค่ 3 จังหวะ: init รัน ครั้งเดียว ตอนเปิด → game.run() วน update() ให้เองทุกเฟรม → ตอนชนะ/แพ้เรา return False เพื่อจบรอบ:
ลองเติมเองให้สุดก่อนนะ — เดี๋ยวเฉลยพร้อมกันในห้องเรียน
"ครบรอบ" บน Bento ไม่ต้องเขียน loop รอบนอกเอง — เฟิร์มแวร์จัดการให้แล้ว:
# mygame.py — โครงทั้งไฟล์: init ครั้งเดียว แล้วเรียก game.run() ครั้งเดียว
game.title("MY GAME") # หน้าเริ่ม: Start=เล่น Back=ออก (ทำ start ให้ในตัว)
score = 0
player = game.Box(game.WIDTH // 2, game.HEIGHT // 2, 32, 32, game.CYAN)
label = game.Text("Score: 0", 12, 10, game.WHITE)
def update():
...
# ชนะ/แพ้จริง -> return False -> แสดง GAME OVER แล้วหยุดรอ
return True
game.run(update, fps=30) # เรียกครั้งเดียวพอ
ปุ่มระบบ game.run() จัดการให้ทุกเกม: BACK = ออกกลับ Playground, START = เริ่มเกมใหม่ (รัน MicroPython ใหม่ตั้งแต่ต้นไฟล์ → โค้ด init ด้านบนรันใหม่ = เกมสด ๆ รอบใหม่)
START รีเซ็ตทั้งบอร์ด (soft reset) → ตัวแปรใน RAM ทั้งหมดถูกล้าง รวมถึง
score
วงจรหนึ่งรอบของเกมเดินแบบนี้ ไล่จากซ้ายไปขวา แล้วพอกด START ก็ย้อนกลับไปเริ่มที่ init ใหม่:

จุดที่อยากให้สังเกต คือ ลูกศรสีแดง ตอนกด START — มันไม่ได้กระโดดกลับไปแค่ตอนเริ่มเล่น แต่ย้อนไปถึง init บนสุดของไฟล์ เหมือนเปิดโปรแกรมใหม่ทั้งตัว เพราะงั้น score กับตัวแปรอื่นใน RAM จึงเริ่มจากศูนย์ทุกครั้ง โดยที่เราไม่ต้องเขียนโค้ดรีเซ็ตเอง
START = soft reset → กลับไป init บนสุดของไฟล์ เอง (ไม่ต้องเขียน while True ครอบ) · เช็คผ่าน: เล่นแพ้ → เห็น GAME OVER + Score → กด START → เกมเริ่มรอบใหม่สด ๆ
อยากเก็บ best score ข้ามรอบ? RAM ถูกล้างตอน START → ต้องเซฟ ลงไฟล์:
try: best = int(open("best.txt").read())...except: best = 0ตอน init · เขียนกลับopen("best.txt","w").write(str(best))ตอนจบรอบ · ถ้ายังไม่ถึงขั้นนั้น ตัด best ทิ้งไปก่อนได้ ทำเกมให้ครบรอบสำคัญกว่า
หัวใจของเกมคือ "ชนแล้วเกิดอะไร" — ที่นี่คือชนเป้าแล้ว +1 แต้ม:
# ช่อง gameplay ที่เราเติมเอง
if game.hit(player, target): # ผู้เล่นแตะเป้า?
score += 1
label.set("Score: %d" % score) # อัปเดตป้ายคะแนน
game.sfx("point") # เสียงได้แต้ม
_move_target() # เด้งเป้าไปจุดใหม่
if score >= WIN_SCORE: # ครบเป้า = ชนะ
game.Text("YOU WIN!", game.WIDTH // 2 - 70,
game.HEIGHT // 2, game.GREEN)
game.sfx("win")
return False
นี่คือ pattern เดียวกับที่น้อง ๆ ใช้ใน Snake (กินอาหาร) และ Flappy (ลอดท่อ) — แค่เปลี่ยน "เงื่อนไขชน" กับ "ผลที่ตามมา" ให้เป็นกติกาของกลุ่มเอง
เสียงในคาบนี้ —
game.sfx("point")ตอนชนเป้าได้แต้ม (เสียง eat ของ Snake / score ของ Flappy) และgame.sfx("gameover")ตอนแพ้ คือ feedback ที่ทำให้เกมของกลุ่ม "มีชีวิต" (กดเล่นฟังได้จริง):
update() — ผังทั้งเกม mygame.pyผัง lifecycle ก่อนหน้าหยุดที่กล่อง "วน update()" เฉย ๆ ทีนี้เรากางข้างในของ update() ออกมาทั้งหมด — หนึ่งเฟรมทำอะไรบ้าง ตั้งแต่อ่านปุ่มจนถึงจุดที่ return False (ชนะ) แล้ว game.run() วนให้เองทุกเฟรม (mygame.py:64)
สัญกรณ์เดียวกับทุกผัง: ▭ฟ้า = ประมวลผล · ▭เขียว = อ่าน input · ◇ม่วง = ตัดสินใจ · ▭เทามน = เริ่ม/จบ · เส้นประ = วนกลับ
ทฤษฎี → โค้ด: AABB ชนกล่อง
game.hit(a,b)→:51(คือ pattern เดียวกับ Snake กินอาหาร / Flappy ลอดท่อ) · FSM ชนะscore ≥ WIN_SCORE→:56·return False= สภาวะจบ (terminal) →:59· ทั้งหมดนี้อยู่ใน 30% ที่เราเขียน ส่วนgame.run()(70%) เป็นตัววนเรียกให้:64
Snake = pattern "กินของแล้วโตขึ้น" (
full_games/snake_full.py:50-55) · Flappy = pattern "scroll ของเข้ามา + ชน = จบ" (full_games/flappy_full.py:54-60)
หลักการเดียว: อ่าน state → ตรวจชน → เปลี่ยน state + ให้รางวัล/ลงโทษ

สร้าง sprite/text ครั้งเดียว นอก update() เท่านั้น:
# mygame.py — สร้างครั้งเดียวก่อนลูป อย่าสร้างใน update()!
player = game.Sprite("ship", 380, 350)
target = game.Sprite("snake_food", 0, 0)
label = game.Text("Score: 0", 12, 10, game.WHITE)
ใน frame loop ให้ใช้ .move_to() / .set() กับ widget ตัวเดิม อย่าเรียก Box/Sprite/Text ซ้ำทุกเฟรม
ขีดจำกัด IPC widget (Python ปิดที่ ~26–32 ตัว):
VERIFY ที่แท้จริง คือเล่นจริง ≥3 รอบติด แล้วไม่ค้าง
อสมการเมื่อกี้แปลเป็นภาพได้ตรง ๆ ลองดูว่า "จำนวน widget สะสม" โตยังไงเมื่อเวลาผ่านไปทีละเฟรม:

Sprite/Text ใหม่ใน update() แต่ละเฟรมบวกเพิ่มเรื่อย ๆ จำนวนจึงไต่ขึ้นเป็นเส้นตรง พอแตะ เส้นประแดง (cap = 32) ระบบก็เปิด widget ใหม่ไม่ได้อีก เกมค้างinit แล้วในลูปแค่ขยับด้วย .move_to() / เปลี่ยนข้อความด้วย .set() จำนวนจึงคงที่ (ในรูปคือ 3 ตัว) อยู่ห่างจาก cap สบาย ๆ เล่นได้ไม่จำกัดเวลาสังเกตว่าเส้นส้มไม่ได้ค้างทันที มันค่อย ๆ ไต่ — เกมเลยดู "ปกติ" อยู่ราว ๆ ครึ่งนาทีก่อนจะค้าง นี่คือเหตุผลที่ต้องเล่นยาว ๆ ตอน VERIFY ไม่ใช่แค่แตะ ๆ แล้วผ่าน
git add mygame.py # ship เฉพาะเกมตัวเอง — bentogame frozen (ห้าม git add .)
git commit -m "team3: final game ครบรอบ + START restart (v1.0)"
git push -u origin team-yourgame
GitHub → Releases → Draft → tag v1.0 → Attach: mygame.py (bentogame frozen ไม่ต้องแนบ) → Publish
Release แนบแค่
mygame.pyพอ เพราะbentogameฝังอยู่ในเฟิร์มแวร์ (frozen) เพื่อน import ได้เลย ไม่ต้องแนบ
อย่าลืมเขียนวิธีรันไว้ใน note: เปิดmygame.pyใน BENTO IDE แล้วกด Program to Device

branch → commit เฉพาะไฟล์
mygame.py→ PR → CI เขียว → review ไขว้ approve → merge → Release v1.0

ทั้งเส้นทางนี้คือสิ่งที่ทีมซอฟต์แวร์จริงทำกันทุกวัน จุดสำคัญอยู่ที่ลูกศร "ส่งมอบ" สีม่วง ก่อนถึงตรงนั้นเป็นงานของทีมเรา (เขียน → ตรวจ → รวม) แต่หลังจากนั้นคนอื่นต้องหยิบไปรันต่อได้เอง โดยที่เราไม่ได้อยู่ด้วย

สลับบอร์ด: กลุ่มข้าง ๆ โหลดเกมของน้อง ๆ → รันเอง ต่อหน้าน้อง ๆ (น้อง ๆ แค่ดู ห้ามช่วยพิมพ์)
1. โหลด mygame.py จากหน้า Release ของเพื่อน
2. เปิด mygame.py ใน BENTO IDE (bentogame frozen แล้ว ไม่ต้องวางเพิ่ม)
3. กด Program to Device -> เล่นได้เลย
เราไม่ใช้
exec(open(...))ในการรัน — ส่งมอบกันด้วย Program to Device ใน BENTO IDE
เพื่อนรันได้เองโดยไม่ต้องถามน้อง ๆ เลย นั่นคือ ส่งมอบสำเร็จ (FINAL ผ่าน)
ถ้ายังติด ให้จดไว้ก่อนว่าเพราะอะไร (ชื่อ sprite ผิด? ลืมบอกวิธีรัน?) แล้วค่อย ๆ แก้ note ให้ครบ

แต่ละกลุ่ม = 1 บูธ (เกมที่ ship เป็น Release แล้ว) → เพื่อนเดินรอบห้อง โหลดไปรันบนบอร์ดตัวเอง เล่น + ให้ feedback → คนละ 1 ข้อติเชิงสร้างสรรค์ → โหวต WINNER

สิ่งที่กำลังจะเกิดในห้องเรา ไม่ใช่กิจกรรมจำลอง นักพัฒนาเกมทั่วโลกรวมตัวกันแบบนี้จริง ๆ ในงานอย่าง Global Game Jam จับกลุ่มกันเล็ก ๆ ตั้งโจทย์ แล้วลงมือทำเกมให้เสร็จในเวลาจำกัด เหมือนที่น้อง ๆ ทำกับ mygame.py

ที่มา: "Global Game Jam 2019 à Arles" — Yannickvernet, CC BY-SA 4.0, Wikimedia Commons
ทักษะชุดเดียวกับที่ฝึกมา 13 คาบ — ทำเกมให้ครบรอบ, ส่งมอบให้คนอื่นรันได้, รับฟังคำติเพื่อแก้ — คือทักษะที่ทีมพัฒนาจริงใช้ ไม่ว่าจะทำเกม แอป หรือผลิตภัณฑ์ฝังตัว
ลองดูบรรยากาศจริงของงานระดับโลก — คนนับหมื่นทั่วโลกจับกลุ่มทำเกมพร้อมกันในสุดสัปดาห์เดียว เหมือนที่เรากำลังจะทำในห้อง:
What is Global Game Jam? — GlobalGameJam (ช่องทางการของงาน)
| อาการ | สาเหตุ | วิธีแก้ |
|---|---|---|
| กด START แล้วไม่เริ่มใหม่ | ดักปุ่ม START เองใน update() |
ลบ if k.start: return False — game.run() จัดการ restart ให้ |
| best หาย/เป็น 0 ทุกครั้ง | เก็บ best ในตัวแปร RAM ที่ START ล้างทิ้ง |
เซฟ best ลงไฟล์ (best.txt) อ่านตอน init |
| เล่น ~30 วิ แล้วค้าง | สร้าง widget ในลูป → ชน 32 cap | สร้างครั้งเดียวตอน init + .move_to/.set |
| เพื่อนรันไม่ได้ | ลืมแนบ mygame.py หรือไม่บอกวิธีรัน |
แนบ mygame.py + เขียน "Program to Device" |
| sprite เป็นกล่องเขียว | desktop sim / fw เก่า | pixel-art เห็นบน บอร์ดจริง เท่านั้น |
หน้าตาของอาการแถวสุดท้าย — ซ้ายคือ "ผิด" (sprite กลายเป็นกล่องเขียวทึบ) ขวาคือ "ถูก" (pixel-art ขึ้นจริงบนบอร์ด):
![]()
ที่มา: ภาพประกอบอาการ fallback (ซ้าย) + ภาพหน้าจอบอร์ดจริง (ขวา) — สื่อประกอบคอร์ส BDH & TESAIoT
sprite = pixel-art เห็นได้บน บอร์ดจริง เท่านั้น (desktop/fw เก่า fallback เป็นกล่องเขียว)
คาบนี้ไม่มี API ใหม่ แต่สิ่งที่ฝึก คือกระบวนการที่ทีมวิศวกรรมจริงใช้กับทุกผลิตภัณฑ์ ไม่ใช่แค่เกม:
ฝั่ง Software Engineering
ฝั่ง Embedded / Python
score หายเพราะอยู่ใน RAM ส่วน best score รอดเพราะเขียนลง ไฟล์ (non-volatile) — เส้นแบ่งความจำที่งานฝังตัวเจอทุกวัน"release" ในงานซอฟต์แวร์คืออะไร ทำไมถึงต่างจาก "เขียนโค้ดเสร็จ" — ตรงกับสิ่งที่เราทำวันนี้พอดี:
What is RELEASE in Software Development? Easy Explanation for Beginners — TS4U
bentogame.pyv1.0 แนบ mygame.py + วิธีรัน (bentogame frozen ไม่ต้องแนบ)ทำเอง 30% — เติมเกมของกลุ่มใน practise_codes/mygame.py↗ (ลองเองให้สุดก่อน เดี๋ยวเฉลยในห้อง) ให้ครบ 3 ช่อง — restart มาจาก START ของ game.run() แล้ว
ผ่านครบทุกข้อ คือจบคอร์ส Developer II แล้ว น้อง ๆ สร้างเกม Embedded ด้วย MicroPython ของตัวเองได้จริง ครบ 70/30 ภูมิใจในตัวน้อง ๆ นะ
หมายเหตุ: DevOps เต็มรูป + AI agent ยกไปต่อใน Developer III
mygame.py — artifact ที่เราจะส่งมอบจริง (อ่านเข้าใจ แล้วพิมพ์เอง)ไฟล์ solution_codes/mygame.py คือเกมเต็มที่เติม 3 ช่องครบแล้ว มีไว้ให้น้อง ๆ เทียบ หลังลงมือเติม practise_codes/mygame.py↗ เอง ไม่ใช่ให้เปิดลอกวางส่ง คะแนนของคาบนี้อยู่ที่ "เพื่อนโหลดไปรันได้จริง" กับใบงาน ไม่ใช่โค้ดตรงเฉลย ลองเติมเองให้สุดสัก 15 นาที ติดตรงไหนค่อยเปิดอ่านให้เข้าใจ ปิดไฟล์ แล้วพิมพ์ใหม่ด้วยมือตัวเอง
ต่างจากคาบก่อน ๆ ตรงที่คาบนี้ ทั้งไฟล์คือ "ของที่จะ Release" เราจึงจะอ่านเฉลยในมุมใหม่: อะไรบ้างที่ทำให้ไฟล์ 65 บรรทัดนี้ "ส่งมอบให้คนอื่นรันได้"
import bentogame as game
PLAYER_SPEED = 8 # ขยับทีละกี่พิกเซลต่อเฟรม
FPS = 30 # ความถี่ของลูป
WIN_SCORE = 5 # ชนเป้าครบเท่านี้ = ชนะ
import bentogame as game (:11) — ทั้งไฟล์พึ่ง bentogame ตัวเดียว ไม่มี import อื่น เพราะ core ฝังอยู่ในเฟิร์มแวร์ (frozen) ตามสไลด์ โครง 70/30 นี่คือเหตุผลตรง ๆ ที่ Release แนบแค่ mygame.py ก็พอ เพื่อน import ได้เลยPLAYER_SPEED / FPS / WIN_SCORE (:13-15) ยกค่าปรับได้ขึ้นบนสุดเป็นชุดเดียว — เพื่อนที่โหลด artifact ไปอยากให้เกมเร็วขึ้นหรือชนะยากขึ้น แก้เลขบรรทัดเดียว ไม่ต้องแกะ update()score ที่เปลี่ยนไปเรื่อยระหว่างเล่น — นิสัยแยก "ค่า" ออกจาก "โครง" ที่ใช้มาตั้งแต่ MVP-1 Snakeartifact ที่ดีคือไฟล์ที่คนรับเปิดมาแล้วเห็น "ปุ่มปรับ" อยู่ตรงหน้า ไม่ต้องเดาว่าตัวเลขไหนแก้ได้
game.title("MY GAME") # หน้าเริ่ม: Start=เล่น Back=ออก (ทำ start ให้ในตัว)
# 1) STATE — เกมต้องจำอะไรบ้าง (สร้างครั้งเดียว ก่อนลูป)
score = 0
player = game.Box(game.WIDTH // 2, game.HEIGHT // 2, 32, 32, game.CYAN)
target = game.Box(120, 120, 28, 28, game.YELLOW)
label = game.Text("Score: 0", 12, 10, game.WHITE)
game.title("MY GAME") (:17) ทำหน้า Start ให้ในตัว (Start=เล่น Back=ออก) เราไม่ต้องเขียนเอง มันเป็นส่วนของ 70% core ที่ย้ำไว้ในสไลด์ 70/30player / target / label (:21-23) ประกาศ นอก update() ครั้งเดียว — ตรงกับสไลด์ ขั้น C กับดัก 32 widget เป๊ะ: widget สร้างครั้งเดียว ในลูปแค่ .move_to() / .set() ถ้าเผลอย้ายสามบรรทัดนี้เข้าไปใน update() เกมจะไต่ชน cap 32 แล้วค้างราวครึ่งนาทีgame.WIDTH // 2, game.HEIGHT // 2 (:21) วางผู้เล่นกลางจอด้วยขนาดจอจริง ไม่ใช่เลขพิกัดตายตัว — artifact จึงรันได้บนบอร์ดทุกขนาดจอ ไม่ผูกกับความละเอียดของเครื่องเรา (นี่คือหน้าตาของคำว่า reproducible)score = 0 (:20) คือ known start state เดียวกับที่สไลด์ ขั้น A พูด: กด START = soft reset → รันไฟล์ใหม่ตั้งแต่บรรทัดนี้ → score กลับเป็น 0 เอง เราไม่ต้องเขียนโค้ดรีเซ็ตสักบรรทัดโค้ด init ที่ดีคือ "รูปถ่ายสถานะเริ่มเกม" กด START ทีไร บอร์ดกลับมายืนที่รูปนี้เป๊ะทุกครั้ง
game.run() — จุดที่ทำให้ "ครบรอบ" # 3) WIN / LOSE — ชนเป้า = +1 แต้ม, ครบ WIN_SCORE = ชนะ
if game.hit(player, target):
score += 1
label.set("Score: %d" % score)
game.sfx("point")
_move_target()
if score >= WIN_SCORE:
game.Text("YOU WIN!", game.WIDTH // 2 - 70, game.HEIGHT // 2, game.GREEN)
game.sfx("win")
return False
return True # เล่นต่อ
game.run(update, fps=FPS)
game.hit(player, target) (:51) คือ AABB ชนกล่อง — pattern เดียวกับสไลด์ ขั้น B (Snake กินอาหาร / Flappy ลอดท่อ) เปลี่ยนแค่ "ชนแล้วเกิดอะไร" ให้เป็นกติกาของกลุ่มเองreturn False เมื่อ score >= WIN_SCORE (:56-59) คือ terminal state ที่ผัง update() ก่อนหน้านี้เรียกว่า "สภาวะจบ" — game.run() เห็น False แล้วขึ้น GAME OVER รอ STARTreturn True (:61) = เล่นต่อเฟรมถัดไป ทุกเฟรมต้องตอบ run() ว่าจบยัง นี่คือ "สัญญา" ระหว่าง 30% ที่เราเขียน กับ 70% ที่วนให้game.run(update, fps=FPS) (:64) เรียก ครั้งเดียว ปิดท้ายไฟล์ เราไม่เขียน while True เอง run() เป็นตัววน + จัดการ Start/Back ให้ — เกมครบรอบโดยเราเขียนแค่กติกา นี่คือทั้งไฟล์ที่จะ shipartifact ที่ส่งมอบไม่มี loop รอบนอกเลย เราส่ง "กติกา" ให้เฟิร์มแวร์ที่เพื่อนมีอยู่แล้วเป็นคนวน นั่นคือเหตุผลที่ไฟล์เล็กจนแนบไฟล์เดียวก็พอ
| ช่อง | บรรทัด | หน้าที่ในเกม | บทบาทตอน Release / ส่งมอบ |
|---|---|---|---|
| ค่าปรับ (const) | :13-15 |
PLAYER_SPEED / FPS / WIN_SCORE |
ปุ่มปรับที่คนรับแก้ได้โดยไม่แตะ logic |
| STATE (init) | :19-23 |
สร้าง widget + score=0 ครั้งเดียว |
known start state ที่ START รีเซ็ตกลับมา · ไม่ชน cap 32 |
| TICK (update) | :38-46 |
อ่าน keys() → player.move |
30% ที่เราเขียน · run() วนให้ทุกเฟรม |
| WIN / LOSE | :51-61 |
hit → +แต้ม → return False / True |
terminal state ที่ทำให้เกม "ครบรอบ" |
| hook | :64 |
game.run(update, fps=FPS) |
เรียกครั้งเดียว = ไม่ต้องแนบ loop · แนบแค่ไฟล์นี้ |
return → run() ไม่รู้ว่าจบเมื่อไหร่Box / Text / keys / hit / sfx / run (70%) bentogame frozen ให้มาแล้ว — นี่คือเหตุผลจริงที่ Release แนบ mygame.py ไฟล์เดียวจบเกมเต็มไม่ใช่โค้ดยาว ๆ มันคือ 3 ช่องเล็ก ๆ ที่ต่อกันถูกที่ แล้วปล่อยให้ core ที่ทุกคนมีเหมือนกันเป็นคนรัน
ที่มา — "ส่งมอบ" ยืนอยู่บนอะไร ย้อนไปสไลด์ 3 ไอเดียของคาบสุดท้าย ข้อ #2 บอกไว้ว่า "ส่งมอบ ≠ ทำเสร็จ" โค้ดเสร็จเมื่อคนอื่นเอาไปรันเองได้ ไอเดียนี้ไม่ได้เพิ่งเกิดวันนี้ มันต่อยอดจากสไลด์ โครง 70/30 โดยตรง: ที่เราแยก 70% core (frozen ในเฟิร์มแวร์) ออกจาก 30% ที่เราเขียน ก็เพื่อให้วันนี้แนบแค่ mygame.py ไฟล์เดียว เพื่อนที่มี core เหมือนกันก็รันต่อได้ ปัญหาดั้งเดิมที่ "ส่งมอบ" มาแก้คือ "โค้ดที่รันได้แค่บนบอร์ดของคนเขียน" ซึ่งไร้ค่ากับทุกคนที่เหลือ
ที่ไป — ทักษะนี้จะโตเป็นอะไร เส้นทาง branch → PR → CI เขียว → merge → tag → Release ในสไลด์ ขั้น D ไม่ใช่พิธีกรรมเฉพาะคาบนี้ มันคือ release lifecycle ที่ทีมซอฟต์แวร์จริงใช้กับทุก build ไม่ว่าจะเป็นแอปมือถือหรือเฟิร์มแวร์ในรถ วันนี้ artifact คือ mygame.py 65 บรรทัด พรุ่งนี้ artifact เดียวกันคือเฟิร์มแวร์ทั้งตัว หรือ container ของบริการที่คนล้านคนใช้ — กระบวนการเท่าเดิม
bentogame ฝังในเฟิร์มแวร์ (frozen) ได้ไหม ถ้า core ไม่ frozen เราจะต้องแนบอะไรเพิ่มใน Release บ้าง แล้วเพื่อนจะรันยากขึ้นแค่ไหน?mygame.py ไปรันได้เอง" ได้ พรุ่งนี้เราจะทำให้ "คนที่เราไม่เคยเจอ ดาวน์โหลดจากอินเทอร์เน็ตไปรัน" ได้ไหม ต่างกันแค่ไหน? (ใบ้: หน้า Release บน GitHub เปิดให้ทุกคนโหลดอยู่แล้ว)score กลับเป็น 0" (known start state จากขั้น A) กับ "เพื่อนโหลดไปรันได้ผลเหมือนเรา" (reproducible) จริง ๆ คือเรื่องเดียวกัน — เริ่มจากสถานะที่คาดเดาได้เสมอ?การหยั่งรู้ที่อยากให้เกิดคือ "ส่งมอบ" ไม่ใช่ขั้นตอนสุดท้ายที่ต่อท้ายงาน แต่คือเหตุผลที่เราออกแบบ 70/30 มาตั้งแต่ต้นคอร์ส
สี่เทคนิควันนี้ไม่ได้อยู่แค่ในคาบเรียน เฟิร์มแวร์กับซอฟต์แวร์จริงใช้ทั้งสี่อย่างนี้ทุกวัน:
.apk / .bin) ให้เครื่องผู้ใช้ดึงไปรันเอง เหมือนเพื่อนโหลด mygame.py จากหน้า Release (ขั้น D–E) — คนใช้ไม่ต้องมี source ก็รันได้bentogame frozen ที่ทำให้ Release แนบไฟล์เดียวพอ (สไลด์ 70/30)v1.0, v1.1 บนทุก Release เพื่อรู้ว่ารันเวอร์ชันไหนและ rollback ได้ (tag v1.0 ใน ขั้น D) เลขเวอร์ชันคือสัญญาว่า "artifact นี้คือชุดนี้ ไม่ใช่ชุดอื่น"เทคนิควันนี้ไม่ใช่กิจกรรมในห้องเรียน มันคือขั้นตอนที่ทีมซอฟต์แวร์ทั่วโลกเดินซ้ำทุกครั้งที่ปล่อยของออกจากมือ
ลองเลือกไปคิดต่อสักข้อ ไม่มีคำตอบเดียวตายตัว ทุกข้อโยงกลับไปหาโปรเจกต์จริงได้:
best.txt) ยังไง ให้ artifact ที่ส่งไปแล้วเพื่อนเล่นแล้วสถิติไม่หาย — ต้องแนบ best.txt ไปด้วยไหม หรือให้โค้ดสร้างเองตอนรันครั้งแรก?mygame.py อยู่ร่วมกันได้ยังไง โดยไม่ชน widget cap 32 และไม่แก้ bentogame — ใครโหลดของใครมาต่อ แล้วตั้งชื่อไฟล์กันชนยังไง?mygame.py ไม่ค้าง / ครบรอบจริง — คำถามนี้พาเราเข้าสู่ DevOps เต็มรูป + AI agent ที่รออยู่ในคอร์ส Developer IIIเลือกมาสักข้อ แล้วเขียนลงใบงานว่า "ถ้าเป็นเรา จะออกแบบยังไง" ไม่ต้องมีคำตอบถูก ขอแค่คิดต่อจากเกมที่น้อง ๆ เพิ่ง ship เอง — ตรงนั้นแหละคือจุดที่คนเขียนเกมกลายเป็นคนส่งมอบซอฟต์แวร์
fit-css