Cafe 283: biến kế hoạch mở quán thành một game 3D trên điện thoại

Trong bài này
Có một kế hoạch mở quán cà phê gần ga Đà Nẵng nằm trong đầu tôi khá lâu. Thay vì chỉ ngồi nhìn bảng tính, tôi nghĩ ra một cách vui hơn để “chạy thử” kế hoạch: viết nó thành game. Kết quả là Cafe 283, một game quản lý quán cà phê 3D chơi ngay trên điện thoại, cài được như app (PWA), vẽ bằng three.js. Bài này kể ngắn gọn game có gì, và quan trọng hơn là chuyện tôi đã làm hỏng bản đầu tiên như thế nào.
Game chơi thế nào
Bạn là chủ quán. Mở cửa lúc sáng sớm, đóng cửa buổi tối, và giữa hai mốc đó là một chuỗi quyết định nhỏ: chỉnh giá menu, tuyển thêm người, nâng cấp máy móc, đặt nguyên liệu, xử lý hàng đợi đang dài ra trước quầy. Màn hình ngang, camera nhìn xuống quán, chạm vào biểu tượng cảnh báo nổi trên quầy hay bàn để sửa nhanh.
Điều tôi muốn nhất là game không chỉ “trông giống” quán cà phê, mà vận hành giống thật ở những chỗ đau:
- Khách theo khung giờ và giờ tàu. Có dân văn phòng, freelancer, dân địa phương, giới trẻ buổi tối, du khách và khách đi tàu. Mỗi nhóm có giờ đông, cỡ nhóm, độ nhạy giá và độ kiên nhẫn riêng. Góc màn hình luôn có chip đếm ngược chuyến tàu, vì khách chờ tàu thường đổ về quán trước giờ tàu cả tiếng.
- Hàng đợi thật. Quầy order, pha chế, tìm bàn, dọn bàn, mỗi khâu là một chỗ có thể nghẽn. Thêm người sai khâu thì vẫn nghẽn.
- Nguyên liệu có hạn dùng. Sữa để được vài ngày, bánh croissant gần như chỉ trong ngày. Đặt nhiều thì hỏng, đặt ít thì hết món, khách đổi món hoặc bỏ về.
- Chi phí đủ kiểu. Lương tính theo giờ làm thực tế (ca đêm, lễ, tăng ca khác nhau), điện theo thiết bị, hoa hồng app giao hàng, thuế cuối tháng.
- Thời tiết và lịch Đà Nẵng. Mùa mưa có ngập, có bão, rồi Tết, các dịp lễ, và những tối pháo hoa lễ hội khiến khách du lịch tăng vọt.

Tôi cố ý giữ con số ở mức mô phỏng chứ không đưa số thật của kế hoạch lên game. Game là nơi thử nhịp vận hành, không phải bảng cân đối kế toán.
Bản v1: đẹp mà sai
Bản đầu tiên tôi giao cho một AI agent viết, và nó ra rất nhanh. Chạy lên là có cảnh 3D, có khách đi lại, có tiền nhảy. Nhìn thì thấy ổn. Vấn đề lộ ra khi tôi đọc kỹ cách nó mô phỏng:
- Thang thời gian lệch nhau. Một giây thật bằng hai phút game, nhưng khách đi bộ, gọi món và pha cà phê chỉ tốn vài giây. Kết quả là khách dịch chuyển gần như tức thời và quầy không bao giờ nghẽn. Không nghẽn thì chẳng có bài toán vận hành nào để chơi.
- Một số việc phụ thuộc
setTimeoutvà random theo từng frame. Cùng một cách chơi, máy mạnh yếu hay tua nhanh chậm lại cho ra kết quả khác nhau. - Thiếu cả loạt thứ làm nên chi phí thật: kho, lương tháng, điện, thuế. Tết thì lặp đều theo chu kỳ máy móc.
- Báo cáo cuối cùng cho thấy lãi rất đẹp ngay tháng đầu tiên. Chính cái “lãi ảo” đó làm tôi nghi ngờ: ai mở quán cũng biết tháng đầu hiếm khi dễ thế.
Bài học vibe coding tôi rút ra: agent rất giỏi làm ra thứ trông đúng. Còn thứ đúng thật thì phải có người đặt câu hỏi, và tốt nhất là có test chứng minh.
Bản v2: mô phỏng trước, đồ họa sau
Tôi viết lại gần như toàn bộ, với một nguyên tắc: lõi mô phỏng tách hẳn khỏi phần vẽ.
- Lõi là JavaScript thuần, không đụng DOM, bước thời gian cố định tính theo phút game, mọi ngẫu nhiên đi qua một seed. Cùng seed, cùng thao tác thì ra cùng kết quả. Lưu game chỉ là một file JSON trạng thái, load giữa ngày chạy tiếp y hệt.
- Phần three.js chỉ đọc trạng thái rồi vẽ. Nhân vật nội suy theo trạng thái, không tự quyết định gì.
- Vì lõi chạy được trong Node, tôi có thể cho nó chạy headless cả năm game trong vài giây. Đó là chỗ cân bằng game thật sự diễn ra: chạy kịch bản chủ quán thụ động, chủ quán chủ động, so sánh, chỉnh thông số, chạy lại. Nhờ vậy tháng đầu trong game mới trở lại gần mức hòa vốn thay vì lãi vô lý.
- Có bộ test cho lõi: tính tất định, save/load, cầu theo giờ, co giãn giá, lương, kho, ngập, chốt tháng. Và một bước kiểm tra trên bản build bằng Chromium headless để chắc không có lỗi JS, service worker chạy và lưu/tải qua reload vẫn đúng.

Phần đồ họa viết trực tiếp bằng three.js, không dùng physics engine vì game quản lý không cần va chạm. Texture sinh bằng code, có chu kỳ ngày đêm, mưa, nước ngập, nhân vật dùng instancing, và chất lượng tự hạ khi máy yếu. Giao diện phủ lên bằng Preact cho nhẹ, cả app đóng gói qua Vite với service worker nên sau lần mở đầu chơi offline được. Tôi chưa đo hiệu năng trên điện thoại thật nhiều, mới chạy trên trình duyệt headless, nên phần này còn là việc phải làm tiếp.
Cách tôi làm việc với agent lần này
Lần này tôi không đưa yêu cầu “làm game đẹp” nữa. Tôi đưa ràng buộc: lõi tất định, tách khỏi render, có test, có script chạy cân bằng. Agent làm rất nhanh khi ranh giới rõ, và tôi dành thời gian vào việc mà máy chưa làm thay được: đọc số liệu ra, hỏi “con số này có hợp lý với một quán thật không”, rồi chỉnh.
Cũng có một lợi ích phụ mà tôi không ngờ tới. Việc biến kế hoạch thành các quy tắc mô phỏng buộc tôi nói rõ những giả định ngầm trong đầu: bao nhiêu khách đến vì tàu, bao nhiêu vì thói quen, giờ nào quầy sẽ nghẹt. Nhiều giả định hóa ra tôi chưa từng viết ra giấy.
Kết
Cafe 283 vẫn là một side project, game vui trước và công cụ suy nghĩ sau. Cái tôi giữ lại nhiều nhất không nằm ở three.js hay PWA, mà ở một điều rất cũ: đẹp thì dễ, đúng mới khó, và vibe coding chỉ thật sự có ích khi mình biết chỗ nào cần kiểm chứng. Nếu bạn cũng đang nhờ agent dựng một mô phỏng, hãy bắt nó chạy headless và nhìn con số trước khi nhìn hình.