Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -179,13 +179,13 @@ The script downloads the **complete Lichess database** (several million puzzles)
**Fundamental principle:** Instead of simply taking the most popular puzzles (which would create redundancies), the script applies a **maximum coverage algorithm by theme**:

```python
def sample_by_themes(tranche, target_per_theme=20, popularity_threshold=90):
def sample_by_themes(tranche, target_per_theme=17, popularity_threshold=90):
```

**Selection steps:**
1. **Theme identification**: Extract all tactical themes (fork, pin, discovered attack, etc.)
2. **Quality filtering**: Priority selection of puzzles with Popularity ≥ 90%
3. **Balanced distribution**: Maximum 20 puzzles per theme to avoid overrepresentation
3. **Balanced distribution**: Maximum 17 puzzles per theme to avoid overrepresentation
4. **Intelligent complement**: Add puzzles with lower popularity for rare themes

### **3. Exhaustive Coverage Guarantee 📊**
Expand Down
2 changes: 1 addition & 1 deletion build_apkg.py
Original file line number Diff line number Diff line change
Expand Up @@ -326,7 +326,7 @@ def build_full(csv_dir: str, output: str) -> None:
import lichess_optimized_puzzles_datasets as ld # pylint: disable=import-outside-toplevel
ld.download_puzzle_db()
ld.decompress_zst()
stats = ld.extract_tranches(ld.CSV_FILE, target_per_theme=20, popularity_threshold=90)
stats = ld.extract_tranches(ld.CSV_FILE, target_per_theme=17, popularity_threshold=90)
build_from_csvs(csv_dir, output, deck_stats=stats)


Expand Down
10 changes: 5 additions & 5 deletions lichess_optimized_puzzles_datasets.py
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,7 @@ def uci_seq_to_san(fen: str, uci_moves: str) -> str:

def sample_by_themes(
tranche: pandas.DataFrame,
target_per_theme: int = 30,
target_per_theme: int = 17,
popularity_threshold: int = 90,
) -> List:
"""
Expand All @@ -185,7 +185,7 @@ def sample_by_themes(
----------
tranche : pandas.DataFrame
DataFrame containing puzzles for a specific ELO range
target_per_theme : int, default=30
target_per_theme : int, default=17
Maximum number of puzzles to select per theme
popularity_threshold : int, default=90
Minimum popularity score for initial selection
Expand Down Expand Up @@ -225,7 +225,7 @@ def sample_by_themes(

def extract_tranches(
csv_file: str,
target_per_theme: int = 30,
target_per_theme: int = 17,
popularity_threshold: int = 90,
) -> Dict[str, Dict]:
"""
Expand All @@ -240,7 +240,7 @@ def extract_tranches(
----------
csv_file : str
Path to the decompressed puzzle database CSV file
target_per_theme : int, default=30
target_per_theme : int, default=17
Maximum puzzles per theme for balanced sampling
popularity_threshold : int, default=90
Minimum popularity threshold for quality filtering
Expand Down Expand Up @@ -413,7 +413,7 @@ def main() -> None:
"""
download_puzzle_db()
decompress_zst()
extract_tranches(CSV_FILE, target_per_theme=20, popularity_threshold=90)
extract_tranches(CSV_FILE, target_per_theme=17, popularity_threshold=90)


if __name__ == "__main__":
Expand Down
36 changes: 24 additions & 12 deletions templates/back.html
Original file line number Diff line number Diff line change
Expand Up @@ -145,12 +145,15 @@ <h1 class="clean-pill">{{Themes}}</h1>
}

/* My Board object */
function ChessBoard(_fenCode, _attrBorder, _colorMode, _attrStyle, _attrClass) {
function ChessBoard(_fenCode, _attrBorder, _colorMode, _attrStyle, _attrClass, _flip) {
this.fenCode = _fenCode;
this.attrBorder = _attrBorder.split(' ');
this.colorMode = _colorMode;
this.attrStyle = _attrStyle;
this.attrClass = _attrClass;
// When the board is shown from Black's side (180° rotation), the coordinate
// labels must be mirrored: bottom-left becomes h1's mirror, not a1.
this.flip = !!_flip;

this.rowCount = 0;
this.colCount = 0;
Expand Down Expand Up @@ -321,6 +324,13 @@ <h1 class="clean-pill">{{Themes}}</h1>
var generatedHTML = '';
this.attrClass = VALUE_CLASS_COLOR_TABLE + (this.attrClass == '' ? '' : ' ' + this.attrClass);

var colsLabels = MERIDA_COORD_COLS.substr(this.coordOriginCol, this.colCount);
var rowsLabels = MERIDA_COORD_ROWS.substr((8 - this.coordOriginRow - this.rowCount) * 6, this.rowCount * 6);
if (this.flip) {
colsLabels = colsLabels.split('').reverse().join('');
rowsLabels = rowsLabels.split('<br/>').filter(function (s) { return s !== ''; }).reverse().join('<br/>') + '<br/>';
}

/* ----- Chess table ---------------------------------------------------------------------------------------------------- */
generatedHTML +=
'<table class="' + this.attrClass + '"' + (this.attrStyle != '' ? ' style="' + this.attrStyle + '"' : '') + '>';
Expand All @@ -332,7 +342,7 @@ <h1 class="clean-pill">{{Themes}}</h1>
generatedHTML +=
(this.isCoordShownLeft || this.isCoordPadded ? '<td>&nbsp;</td>' : '') +
'<td class="cols">' +
MERIDA_COORD_COLS.substr(this.coordOriginCol, this.colCount) +
colsLabels +
'</td>' +
(this.isCoordShownRight || this.isCoordPadded ? '<td>&nbsp;</td>' : '') +
'';
Expand All @@ -354,7 +364,7 @@ <h1 class="clean-pill">{{Themes}}</h1>

if (this.isCoordShownLeft) {
generatedHTML += '<td class="rows">';
generatedHTML += MERIDA_COORD_ROWS.substr((8 - this.coordOriginRow - this.rowCount) * 6, this.rowCount * 6);
generatedHTML += rowsLabels;
generatedHTML += '</td>';
} else if (this.isCoordPadded) {
generatedHTML += '<td class="rows">';
Expand Down Expand Up @@ -433,7 +443,7 @@ <h1 class="clean-pill">{{Themes}}</h1>

if (this.isCoordShownRight) {
generatedHTML += '<td class="rows">';
generatedHTML += MERIDA_COORD_ROWS.substr((8 - this.coordOriginRow - this.rowCount) * 6, this.rowCount * 6);
generatedHTML += rowsLabels;
generatedHTML += '</td>';
} else if (this.isCoordPadded) {
generatedHTML += '<td class="rows">';
Expand All @@ -450,7 +460,7 @@ <h1 class="clean-pill">{{Themes}}</h1>
generatedHTML +=
(this.isCoordShownLeft || this.isCoordPadded ? '<td>&nbsp;</td>' : '') +
'<td class="cols">' +
MERIDA_COORD_COLS.substr(this.coordOriginCol, this.colCount) +
colsLabels +
'</td>' +
(this.isCoordShownRight || this.isCoordPadded ? '<td>&nbsp;</td>' : '') +
'';
Expand Down Expand Up @@ -668,7 +678,8 @@ <h1 class="clean-pill">{{Themes}}</h1>
boardFenCode = chessElt[idx].fenCode;
}

var board = new ChessBoard(boardFenCode, attrBorder, attrMode, attrStyle, attrClass);
var attrFlip = chessElt[idx].getAttribute('data-flip') === '1';
var board = new ChessBoard(boardFenCode, attrBorder, attrMode, attrStyle, attrClass, attrFlip);
var generatedHTML = board.generateHTML();
if (generatedHTML.length == 0) {
/* No board position given. Color mode is kept for upcoming chess tags. */
Expand Down Expand Up @@ -728,7 +739,10 @@ <h1 class="clean-pill">{{Themes}}</h1>
// 5) Minimal DOM updates (textContent avoids accidental HTML parsing)
const fig = document.getElementById('fen_fig');
const toMove = document.getElementById('to_move');
if (fig) fig.textContent = oriented;
if (fig) {
fig.textContent = oriented;
fig.setAttribute('data-flip', side === 'w' ? '0' : '1');
}
if (toMove) toMove.textContent = side === 'w' ? dict.white : dict.black;

// 6) coords button label (shared from same dict, avoids a second lang lookup)
Expand All @@ -742,9 +756,6 @@ <h1 class="clean-pill">{{Themes}}</h1>
const table = document.querySelector('table.chess, table.bwchess');
if (!table) return;

// Measure the real board box (more reliable than the table alone)
const board = table.querySelector('td.board > div.board') || table;

// Reset any previous fitting
table.style.zoom = '';
table.style.transform = '';
Expand All @@ -764,8 +775,9 @@ <h1 class="clean-pill">{{Themes}}</h1>
const maxW = Math.max(50, vw - margin * 2);
const maxH = Math.max(50, vh - overhead - margin * 2);

// Measure actual current board size
const rect = board.getBoundingClientRect();
// Measure the full table (board + coordinate cells) so the right-hand
// coordinates never push the board off-screen when shown.
const rect = table.getBoundingClientRect();

// Fit factor (never upscale; only shrink)
let z = Math.min(maxW / rect.width, maxH / rect.height, 1);
Expand Down
36 changes: 24 additions & 12 deletions templates/front.html
Original file line number Diff line number Diff line change
Expand Up @@ -133,12 +133,15 @@ <h2 id="to_move" class="clean-pill"></h2>
}

/* My Board object */
function ChessBoard(_fenCode, _attrBorder, _colorMode, _attrStyle, _attrClass) {
function ChessBoard(_fenCode, _attrBorder, _colorMode, _attrStyle, _attrClass, _flip) {
this.fenCode = _fenCode;
this.attrBorder = _attrBorder.split(' ');
this.colorMode = _colorMode;
this.attrStyle = _attrStyle;
this.attrClass = _attrClass;
// When the board is shown from Black's side (180° rotation), the coordinate
// labels must be mirrored: bottom-left becomes h1's mirror, not a1.
this.flip = !!_flip;

this.rowCount = 0;
this.colCount = 0;
Expand Down Expand Up @@ -309,6 +312,13 @@ <h2 id="to_move" class="clean-pill"></h2>
var generatedHTML = '';
this.attrClass = VALUE_CLASS_COLOR_TABLE + (this.attrClass == '' ? '' : ' ' + this.attrClass);

var colsLabels = MERIDA_COORD_COLS.substr(this.coordOriginCol, this.colCount);
var rowsLabels = MERIDA_COORD_ROWS.substr((8 - this.coordOriginRow - this.rowCount) * 6, this.rowCount * 6);
if (this.flip) {
colsLabels = colsLabels.split('').reverse().join('');
rowsLabels = rowsLabels.split('<br/>').filter(function (s) { return s !== ''; }).reverse().join('<br/>') + '<br/>';
}

/* ----- Chess table -------------------------------------------------------- */
generatedHTML +=
'<table class="' + this.attrClass + '"' + (this.attrStyle != '' ? ' style="' + this.attrStyle + '"' : '') + '>';
Expand All @@ -320,7 +330,7 @@ <h2 id="to_move" class="clean-pill"></h2>
generatedHTML +=
(this.isCoordShownLeft || this.isCoordPadded ? '<td>&nbsp;</td>' : '') +
'<td class="cols">' +
MERIDA_COORD_COLS.substr(this.coordOriginCol, this.colCount) +
colsLabels +
'</td>' +
(this.isCoordShownRight || this.isCoordPadded ? '<td>&nbsp;</td>' : '') +
'';
Expand All @@ -342,7 +352,7 @@ <h2 id="to_move" class="clean-pill"></h2>

if (this.isCoordShownLeft) {
generatedHTML += '<td class="rows">';
generatedHTML += MERIDA_COORD_ROWS.substr((8 - this.coordOriginRow - this.rowCount) * 6, this.rowCount * 6);
generatedHTML += rowsLabels;
generatedHTML += '</td>';
} else if (this.isCoordPadded) {
generatedHTML += '<td class="rows">';
Expand Down Expand Up @@ -421,7 +431,7 @@ <h2 id="to_move" class="clean-pill"></h2>

if (this.isCoordShownRight) {
generatedHTML += '<td class="rows">';
generatedHTML += MERIDA_COORD_ROWS.substr((8 - this.coordOriginRow - this.rowCount) * 6, this.rowCount * 6);
generatedHTML += rowsLabels;
generatedHTML += '</td>';
} else if (this.isCoordPadded) {
generatedHTML += '<td class="rows">';
Expand All @@ -438,7 +448,7 @@ <h2 id="to_move" class="clean-pill"></h2>
generatedHTML +=
(this.isCoordShownLeft || this.isCoordPadded ? '<td>&nbsp;</td>' : '') +
'<td class="cols">' +
MERIDA_COORD_COLS.substr(this.coordOriginCol, this.colCount) +
colsLabels +
'</td>' +
(this.isCoordShownRight || this.isCoordPadded ? '<td>&nbsp;</td>' : '') +
'';
Expand Down Expand Up @@ -656,7 +666,8 @@ <h2 id="to_move" class="clean-pill"></h2>
boardFenCode = chessElt[idx].fenCode;
}

var board = new ChessBoard(boardFenCode, attrBorder, attrMode, attrStyle, attrClass);
var attrFlip = chessElt[idx].getAttribute('data-flip') === '1';
var board = new ChessBoard(boardFenCode, attrBorder, attrMode, attrStyle, attrClass, attrFlip);
var generatedHTML = board.generateHTML();
if (generatedHTML.length == 0) {
/* No board position given. Color mode is kept for upcoming chess tags. */
Expand Down Expand Up @@ -716,7 +727,10 @@ <h2 id="to_move" class="clean-pill"></h2>
// 5) Minimal DOM updates (textContent avoids accidental HTML parsing)
const fig = document.getElementById('fen_fig');
const toMove = document.getElementById('to_move');
if (fig) fig.textContent = oriented;
if (fig) {
fig.textContent = oriented;
fig.setAttribute('data-flip', side === 'w' ? '0' : '1');
}
if (toMove) toMove.textContent = side === 'w' ? dict.white : dict.black;

// 6) display moves string
Expand All @@ -734,9 +748,6 @@ <h2 id="to_move" class="clean-pill"></h2>
const table = document.querySelector('table.chess, table.bwchess');
if (!table) return;

// Measure the real board box (more reliable than the table alone)
const board = table.querySelector('td.board > div.board') || table;

// Reset any previous fitting
table.style.zoom = '';
table.style.transform = '';
Expand All @@ -756,8 +767,9 @@ <h2 id="to_move" class="clean-pill"></h2>
const maxW = Math.max(50, vw - margin * 2);
const maxH = Math.max(50, vh - overhead - margin * 2);

// Measure actual current board size
const rect = board.getBoundingClientRect();
// Measure the full table (board + coordinate cells) so the right-hand
// coordinates never push the board off-screen when shown.
const rect = table.getBoundingClientRect();

// Fit factor (never upscale; only shrink)
let z = Math.min(maxW / rect.width, maxH / rect.height, 1);
Expand Down
Loading