Game cờ vua chơi với máy trên Android, viết bằng Flutter. Có 4 cấp độ khó (Dễ / Trung bình / Khó / Rất khó) dựa trên thuật toán minimax + alpha-beta pruning chạy trong một isolate riêng (không làm giật giao diện). Tích hợp quảng cáo AdMob (banner + interstitial) để chuẩn bị publish lên Google Play.
lib/
main.dart # Khởi tạo AdMob, chạy app
game/
chess_ai.dart # Engine AI: minimax, đánh giá bàn cờ, các cấp độ
game_controller.dart # Quản lý luật chơi, lượt đi, trạng thái ván cờ, tính điểm
score_service.dart # Công thức tính điểm + đăng nhập/bảng xếp hạng Play Games
screens/
home_screen.dart # Màn hình chọn cấp độ + quân cầm, trạng thái đăng nhập
game_screen.dart # Màn hình chơi: bàn cờ, banner ads, kết quả ván + điểm
services/
ads_service.dart # Bọc AdMob banner/interstitial
widgets/
chess_board.dart # Vẽ bàn cờ 8x8, xử lý chọn quân/phong cấp
tool/
patch_signing.js # CI: gắn signingConfig release vào build.gradle(.kts)
proguard-rules-extra.pro # CI: rule R8 giữ lại class WorkManager cần
games-ids.xml.template # CI: sinh games-ids.xml cho Play Games Services
.github/workflows/
build-apk.yml # Build APK/AAB tự động trên GitHub Actions (cloud)
Thư mục android/ (và ios/, web/…) không được commit vào git —
xem phần “Vì sao không có thư mục android/” bên dưới.
Máy hiện tại chưa cài Flutter SDK / Android Studio, nên mình không tự
flutter create để sinh ra project native được. Thay vào đó:
.github/workflows/build-apk.yml sẽ, mỗi khi chạy trên GitHub Actions:
flutter create --platforms=android . để tự sinh khung project
Android (gradle, AndroidManifest.xml, icon mặc định…).AndroidManifest.xml: quyền INTERNET, AdMob App ID (dùng
ID TEST của Google nếu bạn chưa cấu hình secret thật), và tên app
hiển thị “Chess with AI”.flutter pub get, sinh icon app từ assets/icon/icon.png bằng
flutter_launcher_icons, rồi flutter build apk --release.Ưu điểm: bạn không cần cài đặt gì nặng trên máy để có file APK dùng thử.
Khi nào bạn muốn tùy biến sâu hơn (icon riêng, cấu hình ký release cố định),
nên cài Flutter + Android Studio và bỏ /android/ ra khỏi .gitignore để
commit hẳn thư mục đó, thay vì sinh lại mỗi lần build.
Lưu ý: plugin google_mobile_ads bản mới nhất yêu cầu minSdk 24 trở lên
(Android 7.0+), nên workflow cũng tự nâng minSdkVersion/compileSdk của
project lên 24/36 sau bước flutter create — máy chạy Android cũ hơn 7.0
sẽ không cài được app, đây là đánh đổi hợp lý vì thị phần Android <7.0 hiện
rất nhỏ.
main, workflow tự chạy).chess-app-release-apk.app-release.apk, copy vào điện thoại Android và cài
(cần bật “Cài từ nguồn không xác định” trong Settings).File APK build theo cách này chỉ ký bằng debug key — cài thử trên điện thoại thoải mái, nhưng Google Play sẽ từ chối vì đó là khóa debug công khai. Xem phần ký release bên dưới trước khi publish thật.
Nếu bạn cài Flutter SDK riêng (không cần Android Studio/SDK), bạn vẫn có thể xem giao diện chạy trên trình duyệt để thử nghiệm nhanh, không build ra APK:
flutter pub get
flutter run -d chrome
ca-app-pub-xxxxxxxxxxxxxxxx~yyyyyyyyyy) và thêm
vào GitHub repo: Settings → Secrets and variables → Actions → New
repository secret, tên ADMOB_APP_ID._testBannerAndroid, _testInterstitialAndroid) bằng Ad unit ID thật
của bạn.com.trungchess, project chess_app → applicationId
com.trungchess.chess_app. Muốn đổi thì sửa dòng --org trong
.github/workflows/build-apk.yml.android/key.properties, và trỏ signingConfig của bản release sang
keystore đó trước khi build — cả file APK lẫn AAB đều được ký thật.
Thiếu secret thì build vẫn chạy nhưng quay lại ký bằng debug key như
cũ (chỉ để cài thử, Play Store sẽ từ chối).KEYSTORE_BASE64 | Nội dung file keystore, mã hoá base64 |
| KEYSTORE_PASSWORD | Mật khẩu keystore |
| KEY_ALIAS | Alias của key trong keystore |
| KEY_PASSWORD | Mật khẩu key |key.properties vào git (đã có sẵn trong .gitignore).keytool đi
kèm sẵn):
keytool -genkeypair -v -keystore chess-with-ai-release.jks -alias chesswithai -keyalg RSA -keysize 2048 -validity 10000
rồi mã hoá base64 để dán vào secret KEYSTORE_BASE64 (PowerShell):
[Convert]::ToBase64String([IO.File]::ReadAllBytes("chess-with-ai-release.jks")) | Set-Clipboard
(giờ đã có sẵn trong clipboard, chỉ cần dán trực tiếp vào ô secret).
chess-app-release-apk, cài thử trực tiếp
lên điện thoại) và AAB (chess-app-release-aab, dùng file này để
nộp lên Play Console — Play Store bắt buộc định dạng AAB cho app
mới).assets/icon/icon.png là icon nguồn, workflow tự
chạy dart run flutter_launcher_icons (cấu hình trong pubspec.yaml)
sau bước flutter pub get để sinh icon cho mọi kích thước mipmap.
Muốn đổi icon, chỉ cần thay file assets/icon/icon.png (ảnh vuông,
nên dùng PNG không có góc bo sẵn để Android tự bo theo từng launcher)
rồi push lại.app_id (một dãy số). Đây chính là giá trị cần cho
secret PLAY_GAMES_APP_ID.PLAY_GAMES_APP_ID | app_id lấy từ bước Credentials ở trên |
| PLAY_GAMES_LEADERBOARD_ID | Leaderboard ID vừa tạo |targetSdkVersion/compileSdkVersion đáp ứng yêu cầu mới
nhất của Play Store tại thời điểm nộp (Flutter bản stable mới nhất
thường đã tự set đúng).lib/game/chess_ai.dart định nghĩa 4 mức, tương ứng độ sâu tìm kiếm
minimax + xác suất đi ngẫu nhiên (để mức Dễ luôn có thể thắng được):
| Cấp độ | Độ sâu tìm kiếm | Xác suất đi ngẫu nhiên |
|---|---|---|
| Dễ | 1 | 35% |
| Trung bình | 2 | 15% |
| Khó | 3 | 0% |
| Rất khó | 4 | 0% |
Muốn mạnh hơn nữa (ví dụ dùng engine Stockfish thật thay vì minimax tự
viết), có thể thay chess_ai.dart bằng plugin flutter_stockfish, đổi
lại phức tạp hơn vì cần đóng gói binary theo từng kiến trúc CPU (ARM).
Chỉ thắng (chiếu hết) mới được điểm — thua hoặc hòa thì không. Công
thức trong lib/game/score_service.dart:
điểm = điểm_gốc_theo_cấp_độ × hệ_số_tốc_độ
hệ_số_tốc_độ = clamp(1.5 − số_phút_chơi/20, 0.5, 1.5)
| Cấp độ | Điểm gốc |
|---|---|
| Dễ | 100 |
| Trung bình | 250 |
| Khó | 500 |
| Rất khó | 1000 |
Thắng càng nhanh, hệ số càng gần 1.5 (thắng gần như ngay lập tức); thắng
càng lâu (từ 20 phút trở lên), hệ số giảm dần về mức sàn 0.5 chứ không về
0, nên một ván thắng chậm vẫn luôn được điểm. Điểm cao nhất được lưu cục
bộ trên máy (theo từng cấp độ + tổng thể, qua shared_preferences) và
đồng thời nộp lên bảng xếp hạng Play Games Services nếu người chơi đã
đăng nhập.
git init
git add .
git commit -m "Initial commit: chess app with AI + AdMob"
git branch -M main
git remote add origin <URL_REPO_CUA_BAN>
git push -u origin main