Skip to content

Commit 0751224

Browse files
committed
docs: show feature labels and completion in README status table
1 parent c90e8d6 commit 0751224

File tree

2 files changed

+78
-26
lines changed

2 files changed

+78
-26
lines changed

.github/workflows/scripts/update_readme.py

Lines changed: 56 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,10 @@
1212
from typing import Dict, List, Any, Optional
1313

1414
# Add scripts directory to path to import shared module
15-
SCRIPTS_DIR = os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))), 'scripts')
15+
SCRIPTS_DIR = Path(__file__).resolve().parents[3] / "scripts"
1616
if os.path.exists(SCRIPTS_DIR):
1717
import sys
18-
sys.path.insert(0, SCRIPTS_DIR)
18+
sys.path.insert(0, str(SCRIPTS_DIR))
1919
try:
2020
from chess_metadata import get_metadata
2121
except ImportError:
@@ -88,6 +88,18 @@ def get_metadata(impl_dir): return {}
8888
'test/',
8989
)
9090

91+
FEATURE_CATALOG: List[str] = [
92+
"perft",
93+
"fen",
94+
"ai",
95+
"castling",
96+
"en_passant",
97+
"promotion",
98+
"pgn",
99+
"uci",
100+
"chess960",
101+
]
102+
91103
def find_project_root():
92104
"""Find the project root directory."""
93105
current_dir = os.getcwd()
@@ -237,6 +249,47 @@ def resolve_entrypoint_file(impl_path: str, language: str, meta: Dict[str, Any])
237249
# Pick highest score; for ties prefer shortest path.
238250
return max(candidates, key=lambda rel: (_entrypoint_score(rel), -len(rel)))
239251

252+
def _normalize_feature_name(feature: str) -> str:
253+
"""Normalize feature names for matching and deduplication."""
254+
return feature.strip().lower().replace('-', '_').replace(' ', '_')
255+
256+
def format_feature_summary(meta: Dict[str, Any]) -> str:
257+
"""Render features as labels with completion ratio against the catalog."""
258+
raw_features = meta.get('features', [])
259+
if not isinstance(raw_features, list):
260+
raw_features = []
261+
262+
normalized_catalog = [_normalize_feature_name(item) for item in FEATURE_CATALOG]
263+
catalog_set = set(normalized_catalog)
264+
265+
# Deduplicate while preserving original order from metadata.
266+
normalized_seen = set()
267+
normalized_features: List[str] = []
268+
for item in raw_features:
269+
feature = _normalize_feature_name(str(item))
270+
if feature and feature not in normalized_seen:
271+
normalized_seen.add(feature)
272+
normalized_features.append(feature)
273+
274+
matched_count = sum(1 for feature in normalized_features if feature in catalog_set)
275+
total_count = len(normalized_catalog)
276+
completion_pct = int(round((matched_count / total_count) * 100)) if total_count > 0 else 0
277+
278+
# Show catalog features in canonical order first, then unknown extras.
279+
feature_labels: List[str] = []
280+
for catalog_feature in normalized_catalog:
281+
if catalog_feature in normalized_seen:
282+
feature_labels.append(catalog_feature)
283+
for feature in normalized_features:
284+
if feature not in catalog_set:
285+
feature_labels.append(feature)
286+
287+
if feature_labels:
288+
labels = " ".join(f"`{feature}`" for feature in feature_labels)
289+
return f"{matched_count}/{total_count} ({completion_pct}%) {labels}"
290+
291+
return f"0/{total_count} (0%) -"
292+
240293
def load_performance_data():
241294
"""Load performance benchmark data from individual files"""
242295
project_root = find_project_root()
@@ -442,8 +495,7 @@ def update_readme() -> bool:
442495
mem_disp = f"{int(round(peak_memory))}" if peak_memory > 0 else "-"
443496

444497
# Features
445-
features = meta.get('features', []) if isinstance(meta.get('features'), list) else []
446-
feature_summary = ', '.join(features) if features else '-'
498+
feature_summary = format_feature_summary(meta)
447499

448500
lang_name = f"{lang_emoji} {language.title()}"
449501
loc_display = str(loc_data['loc'])

README.md

Lines changed: 22 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -17,28 +17,28 @@ All implementations have complete feature parity with the following features:
1717

1818
| Language | Status | LOC | Build | Test | Analyze | Memory | Features |
1919
|----------|--------|-----|-------|------|---------|--------|----------|
20-
| 📦 Bun | 🟢 | [669](implementations/bun/chess.js) | 299ms | 192ms | 192ms | 110 MB | - |
21-
| 💠 Crystal | 🟢 | [1692](implementations/crystal/src/chess_engine.cr) | 241ms | 2394ms | 959ms | 526 MB | - |
22-
| 🎯 Dart | 🟡 | [1739](implementations/dart/bin/main.dart) | 542ms | 3109ms | 1277ms | 451 MB | - |
23-
| 🌳 Elm | 🟢 | [1663](implementations/elm/src/ChessEngine.elm) | 1395ms | 358ms | 379ms | 9 MB | - |
24-
| ✨ Gleam | 🟢 | [1917](implementations/gleam/src/chess_engine.gleam) | 288ms | 777ms | 333ms | 110 MB | - |
25-
| 🐹 Go | 🟢 | [2237](implementations/go/chess.go) | 443ms | 1085ms | 1150ms | 115 MB | - |
26-
| 📐 Haskell | 🟢 | [1085](implementations/haskell/src/Main.hs) | 423ms | 229ms | 217ms | 115 MB | - |
27-
| 🪶 Imba | 🟡 | [700](implementations/imba/chess.imba) | 322ms | 195ms | 209ms | 110 MB | - |
28-
| 🟨 Javascript | 🟡 | [682](implementations/javascript/chess.js) | 200ms | 191ms | 192ms | 110 MB | - |
29-
| 🔮 Julia | 🟢 | [1369](implementations/julia/chess.jl) | 235ms | 181ms | 192ms | 112 MB | - |
30-
| 🧡 Kotlin | 🟡 | [1524](implementations/kotlin/src/main/kotlin/ChessEngine.kt) | 213ms | 171ms | 149ms | 111 MB | - |
31-
| 🪐 Lua | 🟢 | [1331](implementations/lua/chess.lua) | 432ms | 264ms | 316ms | - MB | - |
32-
| 🔥 Mojo | 🟢 | [275](implementations/mojo/chess.mojo) | 581ms | 10301ms | 9695ms | 115 MB | - |
33-
| 🦊 Nim | 🟢 | [1105](implementations/nim/chess.nim) | 215ms | 191ms | 184ms | 110 MB | - |
34-
| 🐘 Php | 🟢 | [2016](implementations/php/chess.php) | 711ms | 241ms | 460ms | - MB | - |
35-
| 🐍 Python | 🟡 | [2373](implementations/python/chess.py) | 103ms | 597ms | 209ms | - MB | - |
36-
| 🧠 Rescript | 🟡 | [1678](implementations/rescript/src/Chess.res) | 291ms | 206ms | 192ms | 110 MB | - |
37-
| ❤️ Ruby | 🟡 | [1906](implementations/ruby/chess.rb) | 354ms | 1850ms | 1661ms | - MB | - |
38-
| 🦀 Rust | 🟢 | [1852](implementations/rust/src/main.rs) | 13974ms | 188ms | 197ms | 110 MB | - |
39-
| 🐦 Swift | 🟢 | [856](implementations/swift/src/main.swift) | 369ms | 181ms | 195ms | 114 MB | - |
40-
| 📘 Typescript | 🟡 | [1773](implementations/typescript/src/chess.ts) | 448ms | 945ms | 1919ms | - MB | - |
41-
| ⚡ Zig | 🟢 | [1589](implementations/zig/src/main.zig) | 280ms | 188ms | 187ms | 110 MB | - |
20+
| 📦 Bun | 🟢 | [669](implementations/bun/chess.js) | 299ms | 192ms | 192ms | 110 MB | 6/9 (67%) `perft` `fen` `ai` `castling` `en_passant` `promotion` |
21+
| 💠 Crystal | 🟢 | [1692](implementations/crystal/src/chess_engine.cr) | 241ms | 2394ms | 959ms | 526 MB | 6/9 (67%) `perft` `fen` `ai` `castling` `en_passant` `promotion` |
22+
| 🎯 Dart | 🟡 | [1739](implementations/dart/bin/main.dart) | 542ms | 3109ms | 1277ms | 451 MB | 6/9 (67%) `perft` `fen` `ai` `castling` `en_passant` `promotion` |
23+
| 🌳 Elm | 🟢 | [1663](implementations/elm/src/ChessEngine.elm) | 1395ms | 358ms | 379ms | 9 MB | 6/9 (67%) `perft` `fen` `ai` `castling` `en_passant` `promotion` |
24+
| ✨ Gleam | 🟢 | [1917](implementations/gleam/src/chess_engine.gleam) | 288ms | 777ms | 333ms | 110 MB | 6/9 (67%) `perft` `fen` `ai` `castling` `en_passant` `promotion` |
25+
| 🐹 Go | 🟢 | [2237](implementations/go/chess.go) | 202ms | 1039ms | 1145ms | 115 MB | 6/9 (67%) `perft` `fen` `ai` `castling` `en_passant` `promotion` |
26+
| 📐 Haskell | 🟢 | [1085](implementations/haskell/src/Main.hs) | 423ms | 229ms | 217ms | 115 MB | 6/9 (67%) `perft` `fen` `ai` `castling` `en_passant` `promotion` |
27+
| 🪶 Imba | 🟡 | [700](implementations/imba/chess.imba) | 322ms | 195ms | 209ms | 110 MB | 6/9 (67%) `perft` `fen` `ai` `castling` `en_passant` `promotion` |
28+
| 🟨 Javascript | 🟡 | [682](implementations/javascript/chess.js) | 200ms | 191ms | 192ms | 110 MB | 6/9 (67%) `perft` `fen` `ai` `castling` `en_passant` `promotion` |
29+
| 🔮 Julia | 🟢 | [1369](implementations/julia/chess.jl) | 235ms | 181ms | 192ms | 112 MB | 6/9 (67%) `perft` `fen` `ai` `castling` `en_passant` `promotion` |
30+
| 🧡 Kotlin | 🟡 | [1524](implementations/kotlin/src/main/kotlin/ChessEngine.kt) | 213ms | 171ms | 149ms | 111 MB | 6/9 (67%) `perft` `fen` `ai` `castling` `en_passant` `promotion` |
31+
| 🪐 Lua | 🟢 | [1331](implementations/lua/chess.lua) | 432ms | 264ms | 316ms | - MB | 6/9 (67%) `perft` `fen` `ai` `castling` `en_passant` `promotion` |
32+
| 🔥 Mojo | 🟢 | [275](implementations/mojo/chess.mojo) | 581ms | 10301ms | 9695ms | 115 MB | 6/9 (67%) `perft` `fen` `ai` `castling` `en_passant` `promotion` |
33+
| 🦊 Nim | 🟢 | [1105](implementations/nim/chess.nim) | 215ms | 191ms | 184ms | 110 MB | 6/9 (67%) `perft` `fen` `ai` `castling` `en_passant` `promotion` |
34+
| 🐘 Php | 🟢 | [2016](implementations/php/chess.php) | 711ms | 241ms | 460ms | - MB | 6/9 (67%) `perft` `fen` `ai` `castling` `en_passant` `promotion` |
35+
| 🐍 Python | 🟡 | [2373](implementations/python/chess.py) | 103ms | 597ms | 209ms | - MB | 6/9 (67%) `perft` `fen` `ai` `castling` `en_passant` `promotion` |
36+
| 🧠 Rescript | 🟡 | [1678](implementations/rescript/src/Chess.res) | 291ms | 206ms | 192ms | 110 MB | 6/9 (67%) `perft` `fen` `ai` `castling` `en_passant` `promotion` |
37+
| ❤️ Ruby | 🟡 | [1906](implementations/ruby/chess.rb) | 354ms | 1850ms | 1661ms | - MB | 6/9 (67%) `perft` `fen` `ai` `castling` `en_passant` `promotion` |
38+
| 🦀 Rust | 🟢 | [1852](implementations/rust/src/main.rs) | 13974ms | 188ms | 197ms | 110 MB | 6/9 (67%) `perft` `fen` `ai` `castling` `en_passant` `promotion` |
39+
| 🐦 Swift | 🟢 | [856](implementations/swift/src/main.swift) | 369ms | 181ms | 195ms | 114 MB | 6/9 (67%) `perft` `fen` `ai` `castling` `en_passant` `promotion` |
40+
| 📘 Typescript | 🟡 | [1773](implementations/typescript/src/chess.ts) | 448ms | 945ms | 1919ms | - MB | 6/9 (67%) `perft` `fen` `ai` `castling` `en_passant` `promotion` |
41+
| ⚡ Zig | 🟢 | [1589](implementations/zig/src/main.zig) | 280ms | 188ms | 187ms | 110 MB | 6/9 (67%) `perft` `fen` `ai` `castling` `en_passant` `promotion` |
4242
<!-- status-table-end -->
4343

4444
## 🚀 Quick Start

0 commit comments

Comments
 (0)