torre 0.3.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- torre/__init__.py +6 -0
- torre/__main__.py +4 -0
- torre/analysis/__init__.py +1 -0
- torre/analysis/analyze.py +337 -0
- torre/analysis/board_view.py +195 -0
- torre/analysis/bot_report.py +79 -0
- torre/analysis/engine_service.py +263 -0
- torre/analysis/engines.py +928 -0
- torre/analysis/game_export.py +157 -0
- torre/analysis/game_moves.py +42 -0
- torre/analysis/game_stats.py +518 -0
- torre/analysis/insights.py +394 -0
- torre/analysis/motifs.py +141 -0
- torre/analysis/opening_book.py +97 -0
- torre/analysis/pgn_export.py +82 -0
- torre/analysis/report.py +151 -0
- torre/analysis/scoring.py +313 -0
- torre/cli.py +272 -0
- torre/coaching/__init__.py +1 -0
- torre/coaching/coach.py +524 -0
- torre/coaching/coach_ai.py +132 -0
- torre/coaching/coach_focus.py +162 -0
- torre/coaching/coach_home.py +177 -0
- torre/coaching/coach_ledger.py +364 -0
- torre/coaching/coach_lessons.py +674 -0
- torre/coaching/coach_plan.py +97 -0
- torre/coaching/explain.py +207 -0
- torre/coaching/skills.py +139 -0
- torre/content/chess-library.json +1249 -0
- torre/content/es/awards.json +122 -0
- torre/content/es/messages.json +61 -0
- torre/content/es/themes.json +150 -0
- torre/content/grading.json +13 -0
- torre/content/lessons/README.md +203 -0
- torre/content/lessons/anastasias-mate.json +60 -0
- torre/content/lessons/anderssen-staunton-1851.json +106 -0
- torre/content/lessons/arabian-mate.json +60 -0
- torre/content/lessons/attraction.json +59 -0
- torre/content/lessons/back-rank-mate.json +69 -0
- torre/content/lessons/blackburne-shilling-trap.json +102 -0
- torre/content/lessons/bodens-mate.json +60 -0
- torre/content/lessons/botvinnik-capablanca-1938.json +212 -0
- torre/content/lessons/capablanca-marshall-1909.json +101 -0
- torre/content/lessons/capablanca-tartakower-1924.json +104 -0
- torre/content/lessons/carlsen-anand-2013.json +236 -0
- torre/content/lessons/carlsen-ernst-2004.json +180 -0
- torre/content/lessons/castling.json +71 -0
- torre/content/lessons/check-and-checkmate.json +85 -0
- torre/content/lessons/colle-attack.json +84 -0
- torre/content/lessons/colle-setup.json +71 -0
- torre/content/lessons/deflection.json +59 -0
- torre/content/lessons/discovered-attack.json +60 -0
- torre/content/lessons/double-check.json +60 -0
- torre/content/lessons/doubled-and-backward-pawns.json +71 -0
- torre/content/lessons/dovetail-mate.json +60 -0
- torre/content/lessons/draws.json +76 -0
- torre/content/lessons/elephant-trap.json +127 -0
- torre/content/lessons/en-passant.json +71 -0
- torre/content/lessons/englund-trap.json +120 -0
- torre/content/lessons/es/anastasias-mate.json +49 -0
- torre/content/lessons/es/anderssen-staunton-1851.json +99 -0
- torre/content/lessons/es/arabian-mate.json +49 -0
- torre/content/lessons/es/attraction.json +48 -0
- torre/content/lessons/es/back-rank-mate.json +54 -0
- torre/content/lessons/es/blackburne-shilling-trap.json +55 -0
- torre/content/lessons/es/bodens-mate.json +49 -0
- torre/content/lessons/es/botvinnik-capablanca-1938.json +98 -0
- torre/content/lessons/es/capablanca-marshall-1909.json +90 -0
- torre/content/lessons/es/capablanca-tartakower-1924.json +96 -0
- torre/content/lessons/es/carlsen-anand-2013.json +90 -0
- torre/content/lessons/es/carlsen-ernst-2004.json +92 -0
- torre/content/lessons/es/castling.json +55 -0
- torre/content/lessons/es/check-and-checkmate.json +67 -0
- torre/content/lessons/es/colle-attack.json +61 -0
- torre/content/lessons/es/colle-setup.json +55 -0
- torre/content/lessons/es/deflection.json +48 -0
- torre/content/lessons/es/discovered-attack.json +48 -0
- torre/content/lessons/es/double-check.json +48 -0
- torre/content/lessons/es/doubled-and-backward-pawns.json +55 -0
- torre/content/lessons/es/dovetail-mate.json +49 -0
- torre/content/lessons/es/draws.json +59 -0
- torre/content/lessons/es/elephant-trap.json +69 -0
- torre/content/lessons/es/en-passant.json +55 -0
- torre/content/lessons/es/englund-trap.json +64 -0
- torre/content/lessons/es/fischer-petrosian-1971.json +96 -0
- torre/content/lessons/es/fischer-spassky-1972.json +91 -0
- torre/content/lessons/es/fishing-pole.json +64 -0
- torre/content/lessons/es/fried-liver.json +78 -0
- torre/content/lessons/es/game-of-the-century.json +101 -0
- torre/content/lessons/es/good-and-bad-bishops.json +47 -0
- torre/content/lessons/es/hanging-pieces.json +74 -0
- torre/content/lessons/es/hastings-1895.json +102 -0
- torre/content/lessons/es/hook-mate.json +49 -0
- torre/content/lessons/es/interference.json +48 -0
- torre/content/lessons/es/isolated-pawn.json +73 -0
- torre/content/lessons/es/karpov-unzicker-1974.json +91 -0
- torre/content/lessons/es/kasparov-topalov-1999.json +96 -0
- torre/content/lessons/es/key-squares.json +62 -0
- torre/content/lessons/es/king-and-pawn-vs-king.json +62 -0
- torre/content/lessons/es/knight-fork.json +48 -0
- torre/content/lessons/es/lasker-bauer-1889.json +99 -0
- torre/content/lessons/es/lasker-trap.json +78 -0
- torre/content/lessons/es/legals-mate.json +75 -0
- torre/content/lessons/es/london-model-game.json +81 -0
- torre/content/lessons/es/london-plans.json +55 -0
- torre/content/lessons/es/london-setup.json +61 -0
- torre/content/lessons/es/london-traps.json +55 -0
- torre/content/lessons/es/london-vs-c5-qb6.json +71 -0
- torre/content/lessons/es/lucena.json +48 -0
- torre/content/lessons/es/mikhail-tal.json +88 -0
- torre/content/lessons/es/morphy-bird-1858.json +97 -0
- torre/content/lessons/es/morphy-opera-game.json +79 -0
- torre/content/lessons/es/morphy-paulsen-1857.json +93 -0
- torre/content/lessons/es/morphy-vs-anderssen.json +90 -0
- torre/content/lessons/es/noahs-ark-trap.json +72 -0
- torre/content/lessons/es/open-files.json +55 -0
- torre/content/lessons/es/opening-principles.json +90 -0
- torre/content/lessons/es/opposition.json +54 -0
- torre/content/lessons/es/outposts.json +55 -0
- torre/content/lessons/es/outside-passed-pawn.json +54 -0
- torre/content/lessons/es/overloading.json +48 -0
- torre/content/lessons/es/passed-pawns.json +79 -0
- torre/content/lessons/es/pawn-breakthrough.json +54 -0
- torre/content/lessons/es/pawn-chains.json +53 -0
- torre/content/lessons/es/petrosian-spassky-1966.json +93 -0
- torre/content/lessons/es/philidor-model-game.json +76 -0
- torre/content/lessons/es/philidor-plans.json +55 -0
- torre/content/lessons/es/philidor-position.json +54 -0
- torre/content/lessons/es/philidor-setup.json +55 -0
- torre/content/lessons/es/philidor-traps.json +61 -0
- torre/content/lessons/es/pins.json +40 -0
- torre/content/lessons/es/polgar-berkes-2003.json +92 -0
- torre/content/lessons/es/promotion-basics.json +49 -0
- torre/content/lessons/es/prophylaxis.json +69 -0
- torre/content/lessons/es/queen-mate.json +55 -0
- torre/content/lessons/es/removing-the-defender.json +48 -0
- torre/content/lessons/es/rook-behind-passed-pawn.json +54 -0
- torre/content/lessons/es/rook-mate.json +57 -0
- torre/content/lessons/es/rook-on-the-seventh.json +55 -0
- torre/content/lessons/es/scholars-mate.json +63 -0
- torre/content/lessons/es/skewer.json +48 -0
- torre/content/lessons/es/smothered-mate.json +57 -0
- torre/content/lessons/es/square-of-the-pawn.json +48 -0
- torre/content/lessons/es/st-petersburg-1914.json +96 -0
- torre/content/lessons/es/stalemate.json +55 -0
- torre/content/lessons/es/steinitz-zukertort-1886.json +96 -0
- torre/content/lessons/es/tal-hecht-varna-1962.json +98 -0
- torre/content/lessons/es/tal-larsen-bled-1965.json +90 -0
- torre/content/lessons/es/the-bishop.json +55 -0
- torre/content/lessons/es/the-evergreen-game.json +82 -0
- torre/content/lessons/es/the-immortal-game.json +79 -0
- torre/content/lessons/es/the-king.json +55 -0
- torre/content/lessons/es/the-knight.json +61 -0
- torre/content/lessons/es/the-pawn.json +55 -0
- torre/content/lessons/es/the-queen.json +55 -0
- torre/content/lessons/es/the-rook.json +55 -0
- torre/content/lessons/es/triangulation.json +54 -0
- torre/content/lessons/es/two-bishops-mate.json +57 -0
- torre/content/lessons/es/two-rook-ladder.json +55 -0
- torre/content/lessons/es/weak-squares-and-holes.json +55 -0
- torre/content/lessons/es/wrong-colored-bishop.json +54 -0
- torre/content/lessons/es/x-ray.json +48 -0
- torre/content/lessons/es/zwischenzug.json +48 -0
- torre/content/lessons/fischer-petrosian-1971.json +195 -0
- torre/content/lessons/fischer-spassky-1972.json +204 -0
- torre/content/lessons/fishing-pole.json +112 -0
- torre/content/lessons/fried-liver.json +163 -0
- torre/content/lessons/game-of-the-century.json +217 -0
- torre/content/lessons/good-and-bad-bishops.json +59 -0
- torre/content/lessons/hanging-pieces.json +82 -0
- torre/content/lessons/hastings-1895.json +105 -0
- torre/content/lessons/hook-mate.json +60 -0
- torre/content/lessons/interference.json +59 -0
- torre/content/lessons/isolated-pawn.json +82 -0
- torre/content/lessons/karpov-unzicker-1974.json +209 -0
- torre/content/lessons/kasparov-topalov-1999.json +216 -0
- torre/content/lessons/key-squares.json +81 -0
- torre/content/lessons/king-and-pawn-vs-king.json +81 -0
- torre/content/lessons/knight-fork.json +58 -0
- torre/content/lessons/lasker-bauer-1889.json +104 -0
- torre/content/lessons/lasker-trap.json +160 -0
- torre/content/lessons/legals-mate.json +158 -0
- torre/content/lessons/london-model-game.json +68 -0
- torre/content/lessons/london-plans.json +74 -0
- torre/content/lessons/london-setup.json +80 -0
- torre/content/lessons/london-traps.json +73 -0
- torre/content/lessons/london-vs-c5-qb6.json +96 -0
- torre/content/lessons/lucena.json +59 -0
- torre/content/lessons/mikhail-tal.json +72 -0
- torre/content/lessons/morphy-bird-1858.json +100 -0
- torre/content/lessons/morphy-opera-game.json +69 -0
- torre/content/lessons/morphy-paulsen-1857.json +103 -0
- torre/content/lessons/morphy-vs-anderssen.json +102 -0
- torre/content/lessons/noahs-ark-trap.json +141 -0
- torre/content/lessons/open-files.json +71 -0
- torre/content/lessons/opening-principles.json +96 -0
- torre/content/lessons/opposition.json +68 -0
- torre/content/lessons/outposts.json +71 -0
- torre/content/lessons/outside-passed-pawn.json +69 -0
- torre/content/lessons/overloading.json +59 -0
- torre/content/lessons/passed-pawns.json +85 -0
- torre/content/lessons/pawn-breakthrough.json +70 -0
- torre/content/lessons/pawn-chains.json +71 -0
- torre/content/lessons/petrosian-spassky-1966.json +184 -0
- torre/content/lessons/philidor-model-game.json +66 -0
- torre/content/lessons/philidor-plans.json +73 -0
- torre/content/lessons/philidor-position.json +71 -0
- torre/content/lessons/philidor-setup.json +72 -0
- torre/content/lessons/philidor-traps.json +85 -0
- torre/content/lessons/pins.json +47 -0
- torre/content/lessons/polgar-berkes-2003.json +170 -0
- torre/content/lessons/promotion-basics.json +62 -0
- torre/content/lessons/prophylaxis.json +75 -0
- torre/content/lessons/queen-mate.json +72 -0
- torre/content/lessons/removing-the-defender.json +59 -0
- torre/content/lessons/rook-behind-passed-pawn.json +69 -0
- torre/content/lessons/rook-mate.json +73 -0
- torre/content/lessons/rook-on-the-seventh.json +71 -0
- torre/content/lessons/scholars-mate.json +124 -0
- torre/content/lessons/skewer.json +60 -0
- torre/content/lessons/smothered-mate.json +72 -0
- torre/content/lessons/square-of-the-pawn.json +59 -0
- torre/content/lessons/st-petersburg-1914.json +104 -0
- torre/content/lessons/stalemate.json +70 -0
- torre/content/lessons/steinitz-zukertort-1886.json +103 -0
- torre/content/lessons/tal-hecht-varna-1962.json +231 -0
- torre/content/lessons/tal-larsen-bled-1965.json +194 -0
- torre/content/lessons/the-bishop.json +71 -0
- torre/content/lessons/the-evergreen-game.json +70 -0
- torre/content/lessons/the-immortal-game.json +69 -0
- torre/content/lessons/the-king.json +69 -0
- torre/content/lessons/the-knight.json +80 -0
- torre/content/lessons/the-pawn.json +70 -0
- torre/content/lessons/the-queen.json +70 -0
- torre/content/lessons/the-rook.json +72 -0
- torre/content/lessons/triangulation.json +70 -0
- torre/content/lessons/two-bishops-mate.json +73 -0
- torre/content/lessons/two-rook-ladder.json +71 -0
- torre/content/lessons/units.json +8 -0
- torre/content/lessons/weak-squares-and-holes.json +71 -0
- torre/content/lessons/wrong-colored-bishop.json +70 -0
- torre/content/lessons/x-ray.json +60 -0
- torre/content/lessons/zwischenzug.json +59 -0
- torre/content/opening-book.json +60895 -0
- torre/content/puzzles/anastasia-mate.json +192 -0
- torre/content/puzzles/arabian-mate.json +210 -0
- torre/content/puzzles/attraction.json +227 -0
- torre/content/puzzles/back-rank-mate.json +221 -0
- torre/content/puzzles/bishop-endgame.json +240 -0
- torre/content/puzzles/boden-mate.json +184 -0
- torre/content/puzzles/capturing-defender.json +217 -0
- torre/content/puzzles/clearance.json +221 -0
- torre/content/puzzles/defensive-move.json +222 -0
- torre/content/puzzles/deflection.json +230 -0
- torre/content/puzzles/discovered-attack.json +240 -0
- torre/content/puzzles/double-check.json +223 -0
- torre/content/puzzles/exposed-king.json +221 -0
- torre/content/puzzles/fork.json +240 -0
- torre/content/puzzles/hanging-piece.json +240 -0
- torre/content/puzzles/hook-mate.json +212 -0
- torre/content/puzzles/interference.json +212 -0
- torre/content/puzzles/kingside-attack.json +240 -0
- torre/content/puzzles/knight-endgame.json +240 -0
- torre/content/puzzles/mate-in-1.json +203 -0
- torre/content/puzzles/mate-in-2.json +227 -0
- torre/content/puzzles/mate-in-3.json +240 -0
- torre/content/puzzles/mate-in-4.json +220 -0
- torre/content/puzzles/pawn-endgame.json +240 -0
- torre/content/puzzles/pin.json +234 -0
- torre/content/puzzles/promotion.json +240 -0
- torre/content/puzzles/queen-endgame.json +240 -0
- torre/content/puzzles/quiet-move.json +240 -0
- torre/content/puzzles/rook-endgame.json +240 -0
- torre/content/puzzles/sacrifice.json +240 -0
- torre/content/puzzles/skewer.json +240 -0
- torre/content/puzzles/smothered-mate.json +178 -0
- torre/content/puzzles/trapped-piece.json +216 -0
- torre/content/puzzles/underpromotion.json +202 -0
- torre/content/puzzles/x-ray.json +225 -0
- torre/content/puzzles/zwischenzug.json +212 -0
- torre/content/videos.json +124 -0
- torre/games/__init__.py +1 -0
- torre/games/arena.py +511 -0
- torre/games/chesscom_common.py +88 -0
- torre/games/download_masters.py +80 -0
- torre/games/importer.py +469 -0
- torre/games/masters.py +375 -0
- torre/jsonio.py +141 -0
- torre/learning/__init__.py +1 -0
- torre/learning/achievements.py +407 -0
- torre/learning/drills.py +245 -0
- torre/learning/endgames.py +275 -0
- torre/learning/ladder.py +219 -0
- torre/learning/lessons.py +499 -0
- torre/learning/my_puzzles.py +337 -0
- torre/learning/openings.py +262 -0
- torre/learning/packs.py +157 -0
- torre/learning/puzzles.py +446 -0
- torre/learning/story.py +199 -0
- torre/learning/trainer.py +279 -0
- torre/paths.py +25 -0
- torre/server/__init__.py +1 -0
- torre/server/access.py +47 -0
- torre/server/api_board.py +361 -0
- torre/server/api_coach.py +146 -0
- torre/server/api_engines.py +156 -0
- torre/server/api_games.py +334 -0
- torre/server/api_training.py +370 -0
- torre/server/appdata.py +160 -0
- torre/server/applog.py +43 -0
- torre/server/hosts.py +45 -0
- torre/server/jobs.py +95 -0
- torre/server/routes.py +129 -0
- torre/server/studio.py +282 -0
- torre/tools/__init__.py +3 -0
- torre/tools/build_opening_book.py +43 -0
- torre/tools/build_packs.py +55 -0
- torre/tools/clean_wording.py +103 -0
- torre/tools/download_books.py +882 -0
- torre/tools/game_story.py +186 -0
- torre/tools/import_puzzles.py +104 -0
- torre/tools/lichess_import.py +93 -0
- torre/tools/release_vela.py +76 -0
- torre/tools/repair_game_dates.py +57 -0
- torre/tools/translate_content.py +455 -0
- torre/tools/verify_lessons.py +211 -0
- torre/translations.py +66 -0
- torre/ui/art/avatars/01-3.webp +0 -0
- torre/ui/art/avatars/01-4.webp +0 -0
- torre/ui/art/avatars/01-5.webp +0 -0
- torre/ui/art/avatars/01-7.webp +0 -0
- torre/ui/art/avatars/02-3.webp +0 -0
- torre/ui/art/avatars/02-4.webp +0 -0
- torre/ui/art/avatars/02-6.webp +0 -0
- torre/ui/art/avatars/03-3.webp +0 -0
- torre/ui/art/avatars/03-6.webp +0 -0
- torre/ui/art/avatars/04-1.webp +0 -0
- torre/ui/art/avatars/04-5.webp +0 -0
- torre/ui/art/avatars/04-7.webp +0 -0
- torre/ui/art/avatars/04-8.webp +0 -0
- torre/ui/art/avatars/05-1.webp +0 -0
- torre/ui/art/avatars/05-2.webp +0 -0
- torre/ui/art/avatars/05-4.webp +0 -0
- torre/ui/art/avatars/05-5.webp +0 -0
- torre/ui/art/avatars/06-3.webp +0 -0
- torre/ui/art/avatars/06-7.webp +0 -0
- torre/ui/art/avatars/06-8.webp +0 -0
- torre/ui/art/avatars/07-2.webp +0 -0
- torre/ui/art/avatars/07-3.webp +0 -0
- torre/ui/art/avatars/07-6.webp +0 -0
- torre/ui/art/avatars/07-7.webp +0 -0
- torre/ui/art/avatars/08-2.webp +0 -0
- torre/ui/art/avatars/08-7.webp +0 -0
- torre/ui/art/avatars/10-1.webp +0 -0
- torre/ui/art/avatars/10-8.webp +0 -0
- torre/ui/art/avatars/11-1.webp +0 -0
- torre/ui/art/avatars/11-2.webp +0 -0
- torre/ui/art/avatars/13-6.webp +0 -0
- torre/ui/art/avatars/13-7.webp +0 -0
- torre/ui/art/avatars/14-1.webp +0 -0
- torre/ui/art/avatars/14-3.webp +0 -0
- torre/ui/art/avatars/14-6.webp +0 -0
- torre/ui/art/avatars/15-1.webp +0 -0
- torre/ui/art/avatars/17-4.webp +0 -0
- torre/ui/art/avatars/17-5.webp +0 -0
- torre/ui/art/avatars/17-6.webp +0 -0
- torre/ui/art/avatars/18-6.webp +0 -0
- torre/ui/art/avatars/ukraine-1.webp +0 -0
- torre/ui/art/avatars/ukraine-2.webp +0 -0
- torre/ui/art/avatars/ukraine-3.webp +0 -0
- torre/ui/art/avatars/ukraine-4.webp +0 -0
- torre/ui/art/avatars/ukraine-5.webp +0 -0
- torre/ui/art/avatars/ukraine-6.webp +0 -0
- torre/ui/art/avatars/ukraine-7.webp +0 -0
- torre/ui/art/avatars/ukraine-8.webp +0 -0
- torre/ui/art/badges/back-rank-bronze.webp +0 -0
- torre/ui/art/badges/back-rank-color.webp +0 -0
- torre/ui/art/badges/back-rank-gold.webp +0 -0
- torre/ui/art/badges/back-rank-silver.webp +0 -0
- torre/ui/art/badges/blitz-bronze.webp +0 -0
- torre/ui/art/badges/blitz-color.webp +0 -0
- torre/ui/art/badges/blitz-gold.webp +0 -0
- torre/ui/art/badges/blitz-silver.webp +0 -0
- torre/ui/art/badges/brilliant-bronze.webp +0 -0
- torre/ui/art/badges/brilliant-color.webp +0 -0
- torre/ui/art/badges/brilliant-gold.webp +0 -0
- torre/ui/art/badges/brilliant-silver.webp +0 -0
- torre/ui/art/badges/castle-bronze.webp +0 -0
- torre/ui/art/badges/castle-color.webp +0 -0
- torre/ui/art/badges/castle-gold.webp +0 -0
- torre/ui/art/badges/castle-silver.webp +0 -0
- torre/ui/art/badges/checkmate-bronze.webp +0 -0
- torre/ui/art/badges/checkmate-color.webp +0 -0
- torre/ui/art/badges/checkmate-gold.webp +0 -0
- torre/ui/art/badges/checkmate-silver.webp +0 -0
- torre/ui/art/badges/deflection-bronze.webp +0 -0
- torre/ui/art/badges/deflection-color.webp +0 -0
- torre/ui/art/badges/deflection-gold.webp +0 -0
- torre/ui/art/badges/deflection-silver.webp +0 -0
- torre/ui/art/badges/discovery-bronze.webp +0 -0
- torre/ui/art/badges/discovery-color.webp +0 -0
- torre/ui/art/badges/discovery-gold.webp +0 -0
- torre/ui/art/badges/discovery-silver.webp +0 -0
- torre/ui/art/badges/endgame-bronze.webp +0 -0
- torre/ui/art/badges/endgame-color.webp +0 -0
- torre/ui/art/badges/endgame-gold.webp +0 -0
- torre/ui/art/badges/endgame-silver.webp +0 -0
- torre/ui/art/badges/fork-bronze.webp +0 -0
- torre/ui/art/badges/fork-color.webp +0 -0
- torre/ui/art/badges/fork-gold.webp +0 -0
- torre/ui/art/badges/fork-silver.webp +0 -0
- torre/ui/art/badges/good-sport-bronze.webp +0 -0
- torre/ui/art/badges/good-sport-color.webp +0 -0
- torre/ui/art/badges/good-sport-gold.webp +0 -0
- torre/ui/art/badges/good-sport-silver.webp +0 -0
- torre/ui/art/badges/legend-bronze.webp +0 -0
- torre/ui/art/badges/legend-color.webp +0 -0
- torre/ui/art/badges/legend-gold.webp +0 -0
- torre/ui/art/badges/legend-silver.webp +0 -0
- torre/ui/art/badges/outpost-bronze.webp +0 -0
- torre/ui/art/badges/outpost-color.webp +0 -0
- torre/ui/art/badges/outpost-gold.webp +0 -0
- torre/ui/art/badges/outpost-silver.webp +0 -0
- torre/ui/art/badges/passed-pawn-bronze.webp +0 -0
- torre/ui/art/badges/passed-pawn-color.webp +0 -0
- torre/ui/art/badges/passed-pawn-gold.webp +0 -0
- torre/ui/art/badges/passed-pawn-silver.webp +0 -0
- torre/ui/art/badges/patience-bronze.webp +0 -0
- torre/ui/art/badges/patience-color.webp +0 -0
- torre/ui/art/badges/patience-gold.webp +0 -0
- torre/ui/art/badges/patience-silver.webp +0 -0
- torre/ui/art/badges/pin-bronze.webp +0 -0
- torre/ui/art/badges/pin-color.webp +0 -0
- torre/ui/art/badges/pin-gold.webp +0 -0
- torre/ui/art/badges/pin-silver.webp +0 -0
- torre/ui/art/badges/precision-bronze.webp +0 -0
- torre/ui/art/badges/precision-color.webp +0 -0
- torre/ui/art/badges/precision-gold.webp +0 -0
- torre/ui/art/badges/precision-silver.webp +0 -0
- torre/ui/art/badges/promotion-bronze.webp +0 -0
- torre/ui/art/badges/promotion-color.webp +0 -0
- torre/ui/art/badges/promotion-gold.webp +0 -0
- torre/ui/art/badges/promotion-silver.webp +0 -0
- torre/ui/art/badges/sacrifice-bronze.webp +0 -0
- torre/ui/art/badges/sacrifice-color.webp +0 -0
- torre/ui/art/badges/sacrifice-gold.webp +0 -0
- torre/ui/art/badges/sacrifice-silver.webp +0 -0
- torre/ui/art/badges/scholar-bronze.webp +0 -0
- torre/ui/art/badges/scholar-color.webp +0 -0
- torre/ui/art/badges/scholar-gold.webp +0 -0
- torre/ui/art/badges/scholar-silver.webp +0 -0
- torre/ui/art/badges/skewer-bronze.webp +0 -0
- torre/ui/art/badges/skewer-color.webp +0 -0
- torre/ui/art/badges/skewer-gold.webp +0 -0
- torre/ui/art/badges/skewer-silver.webp +0 -0
- torre/ui/art/badges/summit-bronze.webp +0 -0
- torre/ui/art/badges/summit-color.webp +0 -0
- torre/ui/art/badges/summit-gold.webp +0 -0
- torre/ui/art/badges/summit-silver.webp +0 -0
- torre/ui/art/badges/swindle-bronze.webp +0 -0
- torre/ui/art/badges/swindle-color.webp +0 -0
- torre/ui/art/badges/swindle-gold.webp +0 -0
- torre/ui/art/badges/swindle-silver.webp +0 -0
- torre/ui/art/badges/tactician-bronze.webp +0 -0
- torre/ui/art/badges/tactician-color.webp +0 -0
- torre/ui/art/badges/tactician-gold.webp +0 -0
- torre/ui/art/badges/tactician-silver.webp +0 -0
- torre/ui/art/badges/zugzwang-bronze.webp +0 -0
- torre/ui/art/badges/zugzwang-color.webp +0 -0
- torre/ui/art/badges/zugzwang-gold.webp +0 -0
- torre/ui/art/badges/zugzwang-silver.webp +0 -0
- torre/ui/art/bots/01-1.webp +0 -0
- torre/ui/art/bots/01-2.webp +0 -0
- torre/ui/art/bots/01-3.webp +0 -0
- torre/ui/art/bots/01-4.webp +0 -0
- torre/ui/art/bots/01-5.webp +0 -0
- torre/ui/art/bots/01-6.webp +0 -0
- torre/ui/art/bots/01-7.webp +0 -0
- torre/ui/art/bots/01-8.webp +0 -0
- torre/ui/art/bots/02-1.webp +0 -0
- torre/ui/art/bots/02-2.webp +0 -0
- torre/ui/art/bots/02-3.webp +0 -0
- torre/ui/art/bots/02-4.webp +0 -0
- torre/ui/art/bots/02-5.webp +0 -0
- torre/ui/art/bots/02-6.webp +0 -0
- torre/ui/art/bots/02-7.webp +0 -0
- torre/ui/art/bots/02-8.webp +0 -0
- torre/ui/art/bots/03-2.webp +0 -0
- torre/ui/art/bots/03-3.webp +0 -0
- torre/ui/art/bots/03-4.webp +0 -0
- torre/ui/art/bots/03-5.webp +0 -0
- torre/ui/art/bots/03-6.webp +0 -0
- torre/ui/art/bots/03-7.webp +0 -0
- torre/ui/art/bots/03-8.webp +0 -0
- torre/ui/art/bots/04-1.webp +0 -0
- torre/ui/art/bots/04-2.webp +0 -0
- torre/ui/art/bots/04-3.webp +0 -0
- torre/ui/art/bots/04-4.webp +0 -0
- torre/ui/art/bots/04-5.webp +0 -0
- torre/ui/art/bots/04-6.webp +0 -0
- torre/ui/art/bots/04-7.webp +0 -0
- torre/ui/art/bots/04-8.webp +0 -0
- torre/ui/art/bots/05-1.webp +0 -0
- torre/ui/art/bots/05-2.webp +0 -0
- torre/ui/art/bots/05-3.webp +0 -0
- torre/ui/art/bots/05-4.webp +0 -0
- torre/ui/art/bots/05-5.webp +0 -0
- torre/ui/art/bots/05-7.webp +0 -0
- torre/ui/art/bots/05-8.webp +0 -0
- torre/ui/art/bots/06-1.webp +0 -0
- torre/ui/art/bots/06-2.webp +0 -0
- torre/ui/art/bots/06-3.webp +0 -0
- torre/ui/art/bots/06-4.webp +0 -0
- torre/ui/art/bots/06-5.webp +0 -0
- torre/ui/art/bots/06-7.webp +0 -0
- torre/ui/art/bots/06-8.webp +0 -0
- torre/ui/art/bots/07-1.webp +0 -0
- torre/ui/art/bots/07-2.webp +0 -0
- torre/ui/art/bots/07-3.webp +0 -0
- torre/ui/art/bots/07-4.webp +0 -0
- torre/ui/art/bots/07-5.webp +0 -0
- torre/ui/art/bots/07-6.webp +0 -0
- torre/ui/art/bots/07-7.webp +0 -0
- torre/ui/art/bots/07-8.webp +0 -0
- torre/ui/art/bots/08-1.webp +0 -0
- torre/ui/art/bots/08-2.webp +0 -0
- torre/ui/art/bots/08-3.webp +0 -0
- torre/ui/art/bots/08-4.webp +0 -0
- torre/ui/art/bots/08-5.webp +0 -0
- torre/ui/art/bots/08-6.webp +0 -0
- torre/ui/art/bots/08-7.webp +0 -0
- torre/ui/art/bots/08-8.webp +0 -0
- torre/ui/art/bots/10-1.webp +0 -0
- torre/ui/art/bots/10-2.webp +0 -0
- torre/ui/art/bots/10-3.webp +0 -0
- torre/ui/art/bots/10-4.webp +0 -0
- torre/ui/art/bots/10-6.webp +0 -0
- torre/ui/art/bots/10-7.webp +0 -0
- torre/ui/art/bots/10-8.webp +0 -0
- torre/ui/art/bots/11-1.webp +0 -0
- torre/ui/art/bots/11-2.webp +0 -0
- torre/ui/art/bots/11-3.webp +0 -0
- torre/ui/art/bots/11-4.webp +0 -0
- torre/ui/art/bots/11-5.webp +0 -0
- torre/ui/art/bots/11-6.webp +0 -0
- torre/ui/art/bots/11-7.webp +0 -0
- torre/ui/art/bots/11-8.webp +0 -0
- torre/ui/art/bots/12-1.webp +0 -0
- torre/ui/art/bots/12-2.webp +0 -0
- torre/ui/art/bots/12-3.webp +0 -0
- torre/ui/art/bots/12-4.webp +0 -0
- torre/ui/art/bots/12-7.webp +0 -0
- torre/ui/art/bots/12-8.webp +0 -0
- torre/ui/art/bots/13-2.webp +0 -0
- torre/ui/art/bots/13-3.webp +0 -0
- torre/ui/art/bots/13-4.webp +0 -0
- torre/ui/art/bots/13-5.webp +0 -0
- torre/ui/art/bots/13-6.webp +0 -0
- torre/ui/art/bots/13-7.webp +0 -0
- torre/ui/art/bots/13-8.webp +0 -0
- torre/ui/art/bots/14-1.webp +0 -0
- torre/ui/art/bots/14-2.webp +0 -0
- torre/ui/art/bots/14-3.webp +0 -0
- torre/ui/art/bots/14-4.webp +0 -0
- torre/ui/art/bots/14-6.webp +0 -0
- torre/ui/art/bots/14-7.webp +0 -0
- torre/ui/art/bots/14-8.webp +0 -0
- torre/ui/art/bots/15-1.webp +0 -0
- torre/ui/art/bots/15-2.webp +0 -0
- torre/ui/art/bots/15-3.webp +0 -0
- torre/ui/art/bots/15-4.webp +0 -0
- torre/ui/art/bots/15-5.webp +0 -0
- torre/ui/art/bots/15-6.webp +0 -0
- torre/ui/art/bots/15-7.webp +0 -0
- torre/ui/art/bots/15-8.webp +0 -0
- torre/ui/art/bots/16-1.webp +0 -0
- torre/ui/art/bots/16-2.webp +0 -0
- torre/ui/art/bots/16-3.webp +0 -0
- torre/ui/art/bots/16-4.webp +0 -0
- torre/ui/art/bots/16-5.webp +0 -0
- torre/ui/art/bots/16-6.webp +0 -0
- torre/ui/art/bots/16-7.webp +0 -0
- torre/ui/art/bots/16-8.webp +0 -0
- torre/ui/art/bots/17-1.webp +0 -0
- torre/ui/art/bots/17-2.webp +0 -0
- torre/ui/art/bots/17-3.webp +0 -0
- torre/ui/art/bots/17-4.webp +0 -0
- torre/ui/art/bots/17-5.webp +0 -0
- torre/ui/art/bots/17-6.webp +0 -0
- torre/ui/art/bots/17-7.webp +0 -0
- torre/ui/art/bots/17-8.webp +0 -0
- torre/ui/art/bots/18-1.webp +0 -0
- torre/ui/art/bots/18-2.webp +0 -0
- torre/ui/art/bots/18-3.webp +0 -0
- torre/ui/art/bots/18-4.webp +0 -0
- torre/ui/art/bots/18-5.webp +0 -0
- torre/ui/art/bots/18-6.webp +0 -0
- torre/ui/art/bots/18-7.webp +0 -0
- torre/ui/art/bots/18-8.webp +0 -0
- torre/ui/art/bots/albania-1.webp +0 -0
- torre/ui/art/bots/albania-2.webp +0 -0
- torre/ui/art/bots/albania-3.webp +0 -0
- torre/ui/art/bots/albania-4.webp +0 -0
- torre/ui/art/bots/albania-5.webp +0 -0
- torre/ui/art/bots/albania-6.webp +0 -0
- torre/ui/art/bots/albania-7.webp +0 -0
- torre/ui/art/bots/albania-8.webp +0 -0
- torre/ui/art/bots/arabia-1.webp +0 -0
- torre/ui/art/bots/arabia-2.webp +0 -0
- torre/ui/art/bots/arabia-3.webp +0 -0
- torre/ui/art/bots/arabia-4.webp +0 -0
- torre/ui/art/bots/arabia-5.webp +0 -0
- torre/ui/art/bots/arabia-6.webp +0 -0
- torre/ui/art/bots/arabia-7.webp +0 -0
- torre/ui/art/bots/arabia-8.webp +0 -0
- torre/ui/art/bots/argentina-1.webp +0 -0
- torre/ui/art/bots/argentina-2.webp +0 -0
- torre/ui/art/bots/argentina-3.webp +0 -0
- torre/ui/art/bots/argentina-4.webp +0 -0
- torre/ui/art/bots/argentina-5.webp +0 -0
- torre/ui/art/bots/argentina-6.webp +0 -0
- torre/ui/art/bots/argentina-7.webp +0 -0
- torre/ui/art/bots/argentina-8.webp +0 -0
- torre/ui/art/bots/armenia-1.webp +0 -0
- torre/ui/art/bots/armenia-2.webp +0 -0
- torre/ui/art/bots/armenia-3.webp +0 -0
- torre/ui/art/bots/armenia-4.webp +0 -0
- torre/ui/art/bots/armenia-5.webp +0 -0
- torre/ui/art/bots/armenia-6.webp +0 -0
- torre/ui/art/bots/armenia-7.webp +0 -0
- torre/ui/art/bots/armenia-8.webp +0 -0
- torre/ui/art/bots/brazil-1.webp +0 -0
- torre/ui/art/bots/brazil-2.webp +0 -0
- torre/ui/art/bots/brazil-3.webp +0 -0
- torre/ui/art/bots/brazil-4.webp +0 -0
- torre/ui/art/bots/brazil-5.webp +0 -0
- torre/ui/art/bots/brazil-6.webp +0 -0
- torre/ui/art/bots/brazil-7.webp +0 -0
- torre/ui/art/bots/brazil-8.webp +0 -0
- torre/ui/art/bots/canada-1.webp +0 -0
- torre/ui/art/bots/canada-2.webp +0 -0
- torre/ui/art/bots/canada-3.webp +0 -0
- torre/ui/art/bots/canada-4.webp +0 -0
- torre/ui/art/bots/canada-5.webp +0 -0
- torre/ui/art/bots/canada-6.webp +0 -0
- torre/ui/art/bots/canada-7.webp +0 -0
- torre/ui/art/bots/canada-8.webp +0 -0
- torre/ui/art/bots/chile-1.webp +0 -0
- torre/ui/art/bots/chile-2.webp +0 -0
- torre/ui/art/bots/chile-3.webp +0 -0
- torre/ui/art/bots/chile-4.webp +0 -0
- torre/ui/art/bots/chile-5.webp +0 -0
- torre/ui/art/bots/chile-6.webp +0 -0
- torre/ui/art/bots/chile-7.webp +0 -0
- torre/ui/art/bots/chile-8.webp +0 -0
- torre/ui/art/bots/china-1.webp +0 -0
- torre/ui/art/bots/china-2.webp +0 -0
- torre/ui/art/bots/china-3.webp +0 -0
- torre/ui/art/bots/china-4.webp +0 -0
- torre/ui/art/bots/china-5.webp +0 -0
- torre/ui/art/bots/china-6.webp +0 -0
- torre/ui/art/bots/china-7.webp +0 -0
- torre/ui/art/bots/china-8.webp +0 -0
- torre/ui/art/bots/colombia-1.webp +0 -0
- torre/ui/art/bots/colombia-2.webp +0 -0
- torre/ui/art/bots/colombia-3.webp +0 -0
- torre/ui/art/bots/colombia-4.webp +0 -0
- torre/ui/art/bots/colombia-5.webp +0 -0
- torre/ui/art/bots/colombia-6.webp +0 -0
- torre/ui/art/bots/colombia-7.webp +0 -0
- torre/ui/art/bots/colombia-8.webp +0 -0
- torre/ui/art/bots/diaspora-albania.webp +0 -0
- torre/ui/art/bots/diaspora-arabia.webp +0 -0
- torre/ui/art/bots/diaspora-argentina.webp +0 -0
- torre/ui/art/bots/diaspora-armenia.webp +0 -0
- torre/ui/art/bots/diaspora-brazil.webp +0 -0
- torre/ui/art/bots/diaspora-canada.webp +0 -0
- torre/ui/art/bots/diaspora-chile.webp +0 -0
- torre/ui/art/bots/diaspora-china.webp +0 -0
- torre/ui/art/bots/diaspora-colombia.webp +0 -0
- torre/ui/art/bots/diaspora-el-salvador.webp +0 -0
- torre/ui/art/bots/diaspora-france.webp +0 -0
- torre/ui/art/bots/diaspora-germany.webp +0 -0
- torre/ui/art/bots/diaspora-hawaii.webp +0 -0
- torre/ui/art/bots/diaspora-hungary.webp +0 -0
- torre/ui/art/bots/diaspora-india.webp +0 -0
- torre/ui/art/bots/diaspora-italy.webp +0 -0
- torre/ui/art/bots/diaspora-japan.webp +0 -0
- torre/ui/art/bots/diaspora-korea.webp +0 -0
- torre/ui/art/bots/diaspora-mexico.webp +0 -0
- torre/ui/art/bots/diaspora-netherlands.webp +0 -0
- torre/ui/art/bots/diaspora-nordic.webp +0 -0
- torre/ui/art/bots/diaspora-panama.webp +0 -0
- torre/ui/art/bots/diaspora-peru.webp +0 -0
- torre/ui/art/bots/diaspora-philippines.webp +0 -0
- torre/ui/art/bots/diaspora-portugal.webp +0 -0
- torre/ui/art/bots/diaspora-russia.webp +0 -0
- torre/ui/art/bots/diaspora-serbia.webp +0 -0
- torre/ui/art/bots/diaspora-spain.webp +0 -0
- torre/ui/art/bots/diaspora-sweden.webp +0 -0
- torre/ui/art/bots/diaspora-turkey.webp +0 -0
- torre/ui/art/bots/diaspora-ukraine.webp +0 -0
- torre/ui/art/bots/diaspora-usa.webp +0 -0
- torre/ui/art/bots/el-salvador-1.webp +0 -0
- torre/ui/art/bots/el-salvador-2.webp +0 -0
- torre/ui/art/bots/el-salvador-3.webp +0 -0
- torre/ui/art/bots/el-salvador-4.webp +0 -0
- torre/ui/art/bots/el-salvador-5.webp +0 -0
- torre/ui/art/bots/el-salvador-6.webp +0 -0
- torre/ui/art/bots/el-salvador-7.webp +0 -0
- torre/ui/art/bots/el-salvador-8.webp +0 -0
- torre/ui/art/bots/france-1.webp +0 -0
- torre/ui/art/bots/france-2.webp +0 -0
- torre/ui/art/bots/france-3.webp +0 -0
- torre/ui/art/bots/france-4.webp +0 -0
- torre/ui/art/bots/france-5.webp +0 -0
- torre/ui/art/bots/france-6.webp +0 -0
- torre/ui/art/bots/france-7.webp +0 -0
- torre/ui/art/bots/france-8.webp +0 -0
- torre/ui/art/bots/germany-1.webp +0 -0
- torre/ui/art/bots/germany-2.webp +0 -0
- torre/ui/art/bots/germany-3.webp +0 -0
- torre/ui/art/bots/germany-4.webp +0 -0
- torre/ui/art/bots/germany-5.webp +0 -0
- torre/ui/art/bots/germany-6.webp +0 -0
- torre/ui/art/bots/germany-7.webp +0 -0
- torre/ui/art/bots/germany-8.webp +0 -0
- torre/ui/art/bots/gym-1.webp +0 -0
- torre/ui/art/bots/gym-2.webp +0 -0
- torre/ui/art/bots/gym-3.webp +0 -0
- torre/ui/art/bots/gym-4.webp +0 -0
- torre/ui/art/bots/gym-5.webp +0 -0
- torre/ui/art/bots/gym-6.webp +0 -0
- torre/ui/art/bots/gym-7.webp +0 -0
- torre/ui/art/bots/gym-8.webp +0 -0
- torre/ui/art/bots/hawaii-1.webp +0 -0
- torre/ui/art/bots/hawaii-2.webp +0 -0
- torre/ui/art/bots/hawaii-3.webp +0 -0
- torre/ui/art/bots/hawaii-4.webp +0 -0
- torre/ui/art/bots/hawaii-5.webp +0 -0
- torre/ui/art/bots/hawaii-6.webp +0 -0
- torre/ui/art/bots/hawaii-7.webp +0 -0
- torre/ui/art/bots/hawaii-8.webp +0 -0
- torre/ui/art/bots/hungary-1.webp +0 -0
- torre/ui/art/bots/hungary-2.webp +0 -0
- torre/ui/art/bots/hungary-3.webp +0 -0
- torre/ui/art/bots/hungary-4.webp +0 -0
- torre/ui/art/bots/hungary-5.webp +0 -0
- torre/ui/art/bots/hungary-6.webp +0 -0
- torre/ui/art/bots/hungary-7.webp +0 -0
- torre/ui/art/bots/hungary-8.webp +0 -0
- torre/ui/art/bots/india-1.webp +0 -0
- torre/ui/art/bots/india-2.webp +0 -0
- torre/ui/art/bots/india-3.webp +0 -0
- torre/ui/art/bots/india-4.webp +0 -0
- torre/ui/art/bots/india-5.webp +0 -0
- torre/ui/art/bots/india-6.webp +0 -0
- torre/ui/art/bots/india-7.webp +0 -0
- torre/ui/art/bots/india-8.webp +0 -0
- torre/ui/art/bots/italy-1.webp +0 -0
- torre/ui/art/bots/italy-2.webp +0 -0
- torre/ui/art/bots/italy-3.webp +0 -0
- torre/ui/art/bots/italy-4.webp +0 -0
- torre/ui/art/bots/italy-5.webp +0 -0
- torre/ui/art/bots/italy-6.webp +0 -0
- torre/ui/art/bots/italy-7.webp +0 -0
- torre/ui/art/bots/italy-8.webp +0 -0
- torre/ui/art/bots/japan-1.webp +0 -0
- torre/ui/art/bots/japan-2.webp +0 -0
- torre/ui/art/bots/japan-3.webp +0 -0
- torre/ui/art/bots/japan-4.webp +0 -0
- torre/ui/art/bots/japan-5.webp +0 -0
- torre/ui/art/bots/japan-6.webp +0 -0
- torre/ui/art/bots/japan-7.webp +0 -0
- torre/ui/art/bots/japan-8.webp +0 -0
- torre/ui/art/bots/kitchen-table-1.webp +0 -0
- torre/ui/art/bots/kitchen-table-2.webp +0 -0
- torre/ui/art/bots/kitchen-table-3.webp +0 -0
- torre/ui/art/bots/kitchen-table-4.webp +0 -0
- torre/ui/art/bots/kitchen-table-5.webp +0 -0
- torre/ui/art/bots/kitchen-table-6.webp +0 -0
- torre/ui/art/bots/kitchen-table-7.webp +0 -0
- torre/ui/art/bots/kitchen-table-8.webp +0 -0
- torre/ui/art/bots/korea-1.webp +0 -0
- torre/ui/art/bots/korea-2.webp +0 -0
- torre/ui/art/bots/korea-3.webp +0 -0
- torre/ui/art/bots/korea-4.webp +0 -0
- torre/ui/art/bots/korea-5.webp +0 -0
- torre/ui/art/bots/korea-6.webp +0 -0
- torre/ui/art/bots/korea-7.webp +0 -0
- torre/ui/art/bots/korea-8.webp +0 -0
- torre/ui/art/bots/makers-1.webp +0 -0
- torre/ui/art/bots/makers-2.webp +0 -0
- torre/ui/art/bots/makers-3.webp +0 -0
- torre/ui/art/bots/makers-4.webp +0 -0
- torre/ui/art/bots/makers-5.webp +0 -0
- torre/ui/art/bots/makers-6.webp +0 -0
- torre/ui/art/bots/makers-7.webp +0 -0
- torre/ui/art/bots/makers-8.webp +0 -0
- torre/ui/art/bots/mexico-1.webp +0 -0
- torre/ui/art/bots/mexico-2.webp +0 -0
- torre/ui/art/bots/mexico-3.webp +0 -0
- torre/ui/art/bots/mexico-4.webp +0 -0
- torre/ui/art/bots/mexico-5.webp +0 -0
- torre/ui/art/bots/mexico-6.webp +0 -0
- torre/ui/art/bots/mexico-7.webp +0 -0
- torre/ui/art/bots/mexico-8.webp +0 -0
- torre/ui/art/bots/netherlands-1.webp +0 -0
- torre/ui/art/bots/netherlands-2.webp +0 -0
- torre/ui/art/bots/netherlands-3.webp +0 -0
- torre/ui/art/bots/netherlands-4.webp +0 -0
- torre/ui/art/bots/netherlands-5.webp +0 -0
- torre/ui/art/bots/netherlands-6.webp +0 -0
- torre/ui/art/bots/netherlands-7.webp +0 -0
- torre/ui/art/bots/netherlands-8.webp +0 -0
- torre/ui/art/bots/night-owls-1.webp +0 -0
- torre/ui/art/bots/night-owls-2.webp +0 -0
- torre/ui/art/bots/night-owls-3.webp +0 -0
- torre/ui/art/bots/night-owls-4.webp +0 -0
- torre/ui/art/bots/night-owls-5.webp +0 -0
- torre/ui/art/bots/night-owls-6.webp +0 -0
- torre/ui/art/bots/night-owls-7.webp +0 -0
- torre/ui/art/bots/night-owls-8.webp +0 -0
- torre/ui/art/bots/nordic-1.webp +0 -0
- torre/ui/art/bots/nordic-2.webp +0 -0
- torre/ui/art/bots/nordic-3.webp +0 -0
- torre/ui/art/bots/nordic-4.webp +0 -0
- torre/ui/art/bots/nordic-5.webp +0 -0
- torre/ui/art/bots/nordic-6.webp +0 -0
- torre/ui/art/bots/nordic-7.webp +0 -0
- torre/ui/art/bots/nordic-8.webp +0 -0
- torre/ui/art/bots/panama-1.webp +0 -0
- torre/ui/art/bots/panama-2.webp +0 -0
- torre/ui/art/bots/panama-3.webp +0 -0
- torre/ui/art/bots/panama-4.webp +0 -0
- torre/ui/art/bots/panama-5.webp +0 -0
- torre/ui/art/bots/panama-6.webp +0 -0
- torre/ui/art/bots/panama-7.webp +0 -0
- torre/ui/art/bots/panama-8.webp +0 -0
- torre/ui/art/bots/peru-1.webp +0 -0
- torre/ui/art/bots/peru-2.webp +0 -0
- torre/ui/art/bots/peru-3.webp +0 -0
- torre/ui/art/bots/peru-4.webp +0 -0
- torre/ui/art/bots/peru-5.webp +0 -0
- torre/ui/art/bots/peru-6.webp +0 -0
- torre/ui/art/bots/peru-7.webp +0 -0
- torre/ui/art/bots/peru-8.webp +0 -0
- torre/ui/art/bots/philippines-1.webp +0 -0
- torre/ui/art/bots/philippines-2.webp +0 -0
- torre/ui/art/bots/philippines-3.webp +0 -0
- torre/ui/art/bots/philippines-4.webp +0 -0
- torre/ui/art/bots/philippines-5.webp +0 -0
- torre/ui/art/bots/philippines-6.webp +0 -0
- torre/ui/art/bots/philippines-7.webp +0 -0
- torre/ui/art/bots/philippines-8.webp +0 -0
- torre/ui/art/bots/russia-1.webp +0 -0
- torre/ui/art/bots/russia-2.webp +0 -0
- torre/ui/art/bots/russia-3.webp +0 -0
- torre/ui/art/bots/russia-4.webp +0 -0
- torre/ui/art/bots/russia-5.webp +0 -0
- torre/ui/art/bots/russia-6.webp +0 -0
- torre/ui/art/bots/russia-7.webp +0 -0
- torre/ui/art/bots/russia-8.webp +0 -0
- torre/ui/art/bots/science-lab-1.webp +0 -0
- torre/ui/art/bots/science-lab-2.webp +0 -0
- torre/ui/art/bots/science-lab-3.webp +0 -0
- torre/ui/art/bots/science-lab-4.webp +0 -0
- torre/ui/art/bots/science-lab-5.webp +0 -0
- torre/ui/art/bots/science-lab-6.webp +0 -0
- torre/ui/art/bots/science-lab-7.webp +0 -0
- torre/ui/art/bots/science-lab-8.webp +0 -0
- torre/ui/art/bots/sea-shore-1.webp +0 -0
- torre/ui/art/bots/sea-shore-2.webp +0 -0
- torre/ui/art/bots/sea-shore-3.webp +0 -0
- torre/ui/art/bots/sea-shore-4.webp +0 -0
- torre/ui/art/bots/sea-shore-5.webp +0 -0
- torre/ui/art/bots/sea-shore-6.webp +0 -0
- torre/ui/art/bots/sea-shore-7.webp +0 -0
- torre/ui/art/bots/sea-shore-8.webp +0 -0
- torre/ui/art/bots/seasonal/carnaval/01-1.webp +0 -0
- torre/ui/art/bots/seasonal/carnaval/01-2.webp +0 -0
- torre/ui/art/bots/seasonal/carnaval/01-6.webp +0 -0
- torre/ui/art/bots/seasonal/carnaval/13-2.webp +0 -0
- torre/ui/art/bots/seasonal/carnaval/14-7.webp +0 -0
- torre/ui/art/bots/seasonal/carnaval/15-4.webp +0 -0
- torre/ui/art/bots/seasonal/carnaval/15-7.webp +0 -0
- torre/ui/art/bots/seasonal/carnaval/18-1.webp +0 -0
- torre/ui/art/bots/seasonal/chess-day/08-1.webp +0 -0
- torre/ui/art/bots/seasonal/chess-day/08-6.webp +0 -0
- torre/ui/art/bots/seasonal/chess-day/10-6.webp +0 -0
- torre/ui/art/bots/seasonal/chess-day/12-2.webp +0 -0
- torre/ui/art/bots/seasonal/chess-day/15-3.webp +0 -0
- torre/ui/art/bots/seasonal/chess-day/18-3.webp +0 -0
- torre/ui/art/bots/seasonal/chess-day/18-7.webp +0 -0
- torre/ui/art/bots/seasonal/chess-day/18-8.webp +0 -0
- torre/ui/art/bots/seasonal/christmas/01-1-alt.webp +0 -0
- torre/ui/art/bots/seasonal/christmas/01-1.webp +0 -0
- torre/ui/art/bots/seasonal/christmas/01-2-alt.webp +0 -0
- torre/ui/art/bots/seasonal/christmas/01-2.webp +0 -0
- torre/ui/art/bots/seasonal/christmas/01-3.webp +0 -0
- torre/ui/art/bots/seasonal/christmas/01-4.webp +0 -0
- torre/ui/art/bots/seasonal/christmas/01-5.webp +0 -0
- torre/ui/art/bots/seasonal/christmas/01-6-alt.webp +0 -0
- torre/ui/art/bots/seasonal/christmas/01-6.webp +0 -0
- torre/ui/art/bots/seasonal/christmas/01-7.webp +0 -0
- torre/ui/art/bots/seasonal/christmas/04-1.webp +0 -0
- torre/ui/art/bots/seasonal/christmas/06-8.webp +0 -0
- torre/ui/art/bots/seasonal/christmas/07-6.webp +0 -0
- torre/ui/art/bots/seasonal/christmas/08-1.webp +0 -0
- torre/ui/art/bots/seasonal/christmas/08-6.webp +0 -0
- torre/ui/art/bots/seasonal/christmas/10-6.webp +0 -0
- torre/ui/art/bots/seasonal/christmas/10-8.webp +0 -0
- torre/ui/art/bots/seasonal/christmas/12-2.webp +0 -0
- torre/ui/art/bots/seasonal/christmas/13-2-alt.webp +0 -0
- torre/ui/art/bots/seasonal/christmas/13-2.webp +0 -0
- torre/ui/art/bots/seasonal/christmas/14-7-alt.webp +0 -0
- torre/ui/art/bots/seasonal/christmas/14-7.webp +0 -0
- torre/ui/art/bots/seasonal/christmas/15-3.webp +0 -0
- torre/ui/art/bots/seasonal/christmas/15-4-alt.webp +0 -0
- torre/ui/art/bots/seasonal/christmas/15-4.webp +0 -0
- torre/ui/art/bots/seasonal/christmas/15-7-alt.webp +0 -0
- torre/ui/art/bots/seasonal/christmas/15-7.webp +0 -0
- torre/ui/art/bots/seasonal/christmas/18-1-alt.webp +0 -0
- torre/ui/art/bots/seasonal/christmas/18-1.webp +0 -0
- torre/ui/art/bots/seasonal/christmas/18-3.webp +0 -0
- torre/ui/art/bots/seasonal/christmas/18-7.webp +0 -0
- torre/ui/art/bots/seasonal/christmas/18-8.webp +0 -0
- torre/ui/art/bots/seasonal/cruz-de-mayo/01-3.webp +0 -0
- torre/ui/art/bots/seasonal/cruz-de-mayo/01-4.webp +0 -0
- torre/ui/art/bots/seasonal/cruz-de-mayo/01-5.webp +0 -0
- torre/ui/art/bots/seasonal/cruz-de-mayo/01-7.webp +0 -0
- torre/ui/art/bots/seasonal/cruz-de-mayo/04-1.webp +0 -0
- torre/ui/art/bots/seasonal/cruz-de-mayo/06-8.webp +0 -0
- torre/ui/art/bots/seasonal/cruz-de-mayo/07-6.webp +0 -0
- torre/ui/art/bots/seasonal/cruz-de-mayo/10-8.webp +0 -0
- torre/ui/art/bots/seasonal/dia-de-la-juventud/08-1.webp +0 -0
- torre/ui/art/bots/seasonal/dia-de-la-juventud/08-6.webp +0 -0
- torre/ui/art/bots/seasonal/dia-de-la-juventud/10-6.webp +0 -0
- torre/ui/art/bots/seasonal/dia-de-la-juventud/12-2.webp +0 -0
- torre/ui/art/bots/seasonal/dia-de-la-juventud/15-3.webp +0 -0
- torre/ui/art/bots/seasonal/dia-de-la-juventud/18-3.webp +0 -0
- torre/ui/art/bots/seasonal/dia-de-la-juventud/18-7.webp +0 -0
- torre/ui/art/bots/seasonal/dia-de-la-juventud/18-8.webp +0 -0
- torre/ui/art/bots/seasonal/dia-de-muertos/01-3.webp +0 -0
- torre/ui/art/bots/seasonal/dia-de-muertos/01-4.webp +0 -0
- torre/ui/art/bots/seasonal/dia-de-muertos/01-5.webp +0 -0
- torre/ui/art/bots/seasonal/dia-de-muertos/01-7.webp +0 -0
- torre/ui/art/bots/seasonal/dia-de-muertos/04-1.webp +0 -0
- torre/ui/art/bots/seasonal/dia-de-muertos/06-8.webp +0 -0
- torre/ui/art/bots/seasonal/dia-de-muertos/07-6.webp +0 -0
- torre/ui/art/bots/seasonal/dia-de-muertos/10-8.webp +0 -0
- torre/ui/art/bots/seasonal/dia-del-maestro/01-1.webp +0 -0
- torre/ui/art/bots/seasonal/dia-del-maestro/01-2.webp +0 -0
- torre/ui/art/bots/seasonal/dia-del-maestro/01-6.webp +0 -0
- torre/ui/art/bots/seasonal/dia-del-maestro/08-1.webp +0 -0
- torre/ui/art/bots/seasonal/dia-del-maestro/08-6.webp +0 -0
- torre/ui/art/bots/seasonal/dia-del-maestro/10-6.webp +0 -0
- torre/ui/art/bots/seasonal/dia-del-maestro/12-2.webp +0 -0
- torre/ui/art/bots/seasonal/dia-del-maestro/13-2.webp +0 -0
- torre/ui/art/bots/seasonal/dia-del-maestro/14-7.webp +0 -0
- torre/ui/art/bots/seasonal/dia-del-maestro/15-3.webp +0 -0
- torre/ui/art/bots/seasonal/dia-del-maestro/15-4.webp +0 -0
- torre/ui/art/bots/seasonal/dia-del-maestro/15-7.webp +0 -0
- torre/ui/art/bots/seasonal/dia-del-maestro/18-1.webp +0 -0
- torre/ui/art/bots/seasonal/dia-del-maestro/18-3.webp +0 -0
- torre/ui/art/bots/seasonal/dia-del-maestro/18-7.webp +0 -0
- torre/ui/art/bots/seasonal/dia-del-maestro/18-8.webp +0 -0
- torre/ui/art/bots/seasonal/diablos-de-yare/01-1.webp +0 -0
- torre/ui/art/bots/seasonal/diablos-de-yare/01-2.webp +0 -0
- torre/ui/art/bots/seasonal/diablos-de-yare/01-6.webp +0 -0
- torre/ui/art/bots/seasonal/diablos-de-yare/13-2.webp +0 -0
- torre/ui/art/bots/seasonal/diablos-de-yare/14-7.webp +0 -0
- torre/ui/art/bots/seasonal/diablos-de-yare/15-4.webp +0 -0
- torre/ui/art/bots/seasonal/diablos-de-yare/15-7.webp +0 -0
- torre/ui/art/bots/seasonal/diablos-de-yare/18-1.webp +0 -0
- torre/ui/art/bots/seasonal/halloween/01-1-alt.webp +0 -0
- torre/ui/art/bots/seasonal/halloween/01-1.webp +0 -0
- torre/ui/art/bots/seasonal/halloween/01-2-alt.webp +0 -0
- torre/ui/art/bots/seasonal/halloween/01-2.webp +0 -0
- torre/ui/art/bots/seasonal/halloween/01-3.webp +0 -0
- torre/ui/art/bots/seasonal/halloween/01-4.webp +0 -0
- torre/ui/art/bots/seasonal/halloween/01-5.webp +0 -0
- torre/ui/art/bots/seasonal/halloween/01-6-alt.webp +0 -0
- torre/ui/art/bots/seasonal/halloween/01-6.webp +0 -0
- torre/ui/art/bots/seasonal/halloween/01-7.webp +0 -0
- torre/ui/art/bots/seasonal/halloween/04-1.webp +0 -0
- torre/ui/art/bots/seasonal/halloween/06-8.webp +0 -0
- torre/ui/art/bots/seasonal/halloween/07-6.webp +0 -0
- torre/ui/art/bots/seasonal/halloween/08-1.webp +0 -0
- torre/ui/art/bots/seasonal/halloween/08-6.webp +0 -0
- torre/ui/art/bots/seasonal/halloween/10-6.webp +0 -0
- torre/ui/art/bots/seasonal/halloween/10-8.webp +0 -0
- torre/ui/art/bots/seasonal/halloween/12-2.webp +0 -0
- torre/ui/art/bots/seasonal/halloween/13-2-alt.webp +0 -0
- torre/ui/art/bots/seasonal/halloween/13-2.webp +0 -0
- torre/ui/art/bots/seasonal/halloween/14-7-alt.webp +0 -0
- torre/ui/art/bots/seasonal/halloween/14-7.webp +0 -0
- torre/ui/art/bots/seasonal/halloween/15-3.webp +0 -0
- torre/ui/art/bots/seasonal/halloween/15-4-alt.webp +0 -0
- torre/ui/art/bots/seasonal/halloween/15-4.webp +0 -0
- torre/ui/art/bots/seasonal/halloween/15-7-alt.webp +0 -0
- torre/ui/art/bots/seasonal/halloween/15-7.webp +0 -0
- torre/ui/art/bots/seasonal/halloween/18-1-alt.webp +0 -0
- torre/ui/art/bots/seasonal/halloween/18-1.webp +0 -0
- torre/ui/art/bots/seasonal/halloween/18-3.webp +0 -0
- torre/ui/art/bots/seasonal/halloween/18-7.webp +0 -0
- torre/ui/art/bots/seasonal/halloween/18-8.webp +0 -0
- torre/ui/art/bots/seasonal/independencia/01-1.webp +0 -0
- torre/ui/art/bots/seasonal/independencia/01-2.webp +0 -0
- torre/ui/art/bots/seasonal/independencia/01-6.webp +0 -0
- torre/ui/art/bots/seasonal/independencia/13-2.webp +0 -0
- torre/ui/art/bots/seasonal/independencia/14-7.webp +0 -0
- torre/ui/art/bots/seasonal/independencia/15-4.webp +0 -0
- torre/ui/art/bots/seasonal/independencia/15-7.webp +0 -0
- torre/ui/art/bots/seasonal/independencia/18-1.webp +0 -0
- torre/ui/art/bots/seasonal/leyendas/08-1.webp +0 -0
- torre/ui/art/bots/seasonal/leyendas/08-6.webp +0 -0
- torre/ui/art/bots/seasonal/leyendas/10-6.webp +0 -0
- torre/ui/art/bots/seasonal/leyendas/12-2.webp +0 -0
- torre/ui/art/bots/seasonal/leyendas/15-3.webp +0 -0
- torre/ui/art/bots/seasonal/leyendas/18-3.webp +0 -0
- torre/ui/art/bots/seasonal/leyendas/18-7.webp +0 -0
- torre/ui/art/bots/seasonal/leyendas/18-8.webp +0 -0
- torre/ui/art/bots/seasonal/lunar-new-year/01-3.webp +0 -0
- torre/ui/art/bots/seasonal/lunar-new-year/01-4.webp +0 -0
- torre/ui/art/bots/seasonal/lunar-new-year/01-5.webp +0 -0
- torre/ui/art/bots/seasonal/lunar-new-year/01-7.webp +0 -0
- torre/ui/art/bots/seasonal/lunar-new-year/04-1.webp +0 -0
- torre/ui/art/bots/seasonal/lunar-new-year/06-8.webp +0 -0
- torre/ui/art/bots/seasonal/lunar-new-year/07-6.webp +0 -0
- torre/ui/art/bots/seasonal/lunar-new-year/10-8.webp +0 -0
- torre/ui/art/bots/seasonal/new-year/01-1.webp +0 -0
- torre/ui/art/bots/seasonal/new-year/01-2.webp +0 -0
- torre/ui/art/bots/seasonal/new-year/01-6.webp +0 -0
- torre/ui/art/bots/seasonal/new-year/13-2.webp +0 -0
- torre/ui/art/bots/seasonal/new-year/14-7.webp +0 -0
- torre/ui/art/bots/seasonal/new-year/15-4.webp +0 -0
- torre/ui/art/bots/seasonal/new-year/15-7.webp +0 -0
- torre/ui/art/bots/seasonal/new-year/18-1.webp +0 -0
- torre/ui/art/bots/seasonal/semana-santa/01-3.webp +0 -0
- torre/ui/art/bots/seasonal/semana-santa/01-4.webp +0 -0
- torre/ui/art/bots/seasonal/semana-santa/01-5.webp +0 -0
- torre/ui/art/bots/seasonal/semana-santa/01-7.webp +0 -0
- torre/ui/art/bots/seasonal/semana-santa/04-1.webp +0 -0
- torre/ui/art/bots/seasonal/semana-santa/06-8.webp +0 -0
- torre/ui/art/bots/seasonal/semana-santa/07-6.webp +0 -0
- torre/ui/art/bots/seasonal/semana-santa/10-8.webp +0 -0
- torre/ui/art/bots/seasonal/spring/01-3.webp +0 -0
- torre/ui/art/bots/seasonal/spring/01-4.webp +0 -0
- torre/ui/art/bots/seasonal/spring/01-5.webp +0 -0
- torre/ui/art/bots/seasonal/spring/01-7.webp +0 -0
- torre/ui/art/bots/seasonal/spring/04-1.webp +0 -0
- torre/ui/art/bots/seasonal/spring/06-8.webp +0 -0
- torre/ui/art/bots/seasonal/spring/07-6.webp +0 -0
- torre/ui/art/bots/seasonal/spring/10-8.webp +0 -0
- torre/ui/art/bots/seasonal/st-patricks/08-1.webp +0 -0
- torre/ui/art/bots/seasonal/st-patricks/08-6.webp +0 -0
- torre/ui/art/bots/seasonal/st-patricks/10-6.webp +0 -0
- torre/ui/art/bots/seasonal/st-patricks/12-2.webp +0 -0
- torre/ui/art/bots/seasonal/st-patricks/15-3.webp +0 -0
- torre/ui/art/bots/seasonal/st-patricks/18-3.webp +0 -0
- torre/ui/art/bots/seasonal/st-patricks/18-7.webp +0 -0
- torre/ui/art/bots/seasonal/st-patricks/18-8.webp +0 -0
- torre/ui/art/bots/seasonal/summer/08-1.webp +0 -0
- torre/ui/art/bots/seasonal/summer/08-6.webp +0 -0
- torre/ui/art/bots/seasonal/summer/10-6.webp +0 -0
- torre/ui/art/bots/seasonal/summer/12-2.webp +0 -0
- torre/ui/art/bots/seasonal/summer/15-3.webp +0 -0
- torre/ui/art/bots/seasonal/summer/18-3.webp +0 -0
- torre/ui/art/bots/seasonal/summer/18-7.webp +0 -0
- torre/ui/art/bots/seasonal/summer/18-8.webp +0 -0
- torre/ui/art/bots/seasonal/thanksgiving/01-3.webp +0 -0
- torre/ui/art/bots/seasonal/thanksgiving/01-4.webp +0 -0
- torre/ui/art/bots/seasonal/thanksgiving/01-5.webp +0 -0
- torre/ui/art/bots/seasonal/thanksgiving/01-7.webp +0 -0
- torre/ui/art/bots/seasonal/thanksgiving/04-1.webp +0 -0
- torre/ui/art/bots/seasonal/thanksgiving/06-8.webp +0 -0
- torre/ui/art/bots/seasonal/thanksgiving/07-6.webp +0 -0
- torre/ui/art/bots/seasonal/thanksgiving/10-8.webp +0 -0
- torre/ui/art/bots/seasonal/valentines/08-1.webp +0 -0
- torre/ui/art/bots/seasonal/valentines/08-6.webp +0 -0
- torre/ui/art/bots/seasonal/valentines/10-6.webp +0 -0
- torre/ui/art/bots/seasonal/valentines/12-2.webp +0 -0
- torre/ui/art/bots/seasonal/valentines/15-3.webp +0 -0
- torre/ui/art/bots/seasonal/valentines/18-3.webp +0 -0
- torre/ui/art/bots/seasonal/valentines/18-7.webp +0 -0
- torre/ui/art/bots/seasonal/valentines/18-8.webp +0 -0
- torre/ui/art/bots/serbia-1.webp +0 -0
- torre/ui/art/bots/serbia-2.webp +0 -0
- torre/ui/art/bots/serbia-3.webp +0 -0
- torre/ui/art/bots/serbia-4.webp +0 -0
- torre/ui/art/bots/serbia-5.webp +0 -0
- torre/ui/art/bots/serbia-6.webp +0 -0
- torre/ui/art/bots/serbia-7.webp +0 -0
- torre/ui/art/bots/serbia-8.webp +0 -0
- torre/ui/art/bots/spain-1.webp +0 -0
- torre/ui/art/bots/spain-2.webp +0 -0
- torre/ui/art/bots/spain-3.webp +0 -0
- torre/ui/art/bots/spain-4.webp +0 -0
- torre/ui/art/bots/spain-5.webp +0 -0
- torre/ui/art/bots/spain-6.webp +0 -0
- torre/ui/art/bots/spain-7.webp +0 -0
- torre/ui/art/bots/spain-8.webp +0 -0
- torre/ui/art/bots/stage-1.webp +0 -0
- torre/ui/art/bots/stage-2.webp +0 -0
- torre/ui/art/bots/stage-3.webp +0 -0
- torre/ui/art/bots/stage-4.webp +0 -0
- torre/ui/art/bots/stage-5.webp +0 -0
- torre/ui/art/bots/stage-6.webp +0 -0
- torre/ui/art/bots/stage-7.webp +0 -0
- torre/ui/art/bots/stage-8.webp +0 -0
- torre/ui/art/bots/sweden-1.webp +0 -0
- torre/ui/art/bots/sweden-2.webp +0 -0
- torre/ui/art/bots/sweden-3.webp +0 -0
- torre/ui/art/bots/sweden-4.webp +0 -0
- torre/ui/art/bots/sweden-5.webp +0 -0
- torre/ui/art/bots/sweden-6.webp +0 -0
- torre/ui/art/bots/sweden-7.webp +0 -0
- torre/ui/art/bots/sweden-8.webp +0 -0
- torre/ui/art/bots/turkey-1.webp +0 -0
- torre/ui/art/bots/turkey-2.webp +0 -0
- torre/ui/art/bots/turkey-3.webp +0 -0
- torre/ui/art/bots/turkey-4.webp +0 -0
- torre/ui/art/bots/turkey-5.webp +0 -0
- torre/ui/art/bots/turkey-6.webp +0 -0
- torre/ui/art/bots/turkey-7.webp +0 -0
- torre/ui/art/bots/turkey-8.webp +0 -0
- torre/ui/art/bots/ukraine-1.webp +0 -0
- torre/ui/art/bots/ukraine-2.webp +0 -0
- torre/ui/art/bots/ukraine-3.webp +0 -0
- torre/ui/art/bots/ukraine-4.webp +0 -0
- torre/ui/art/bots/ukraine-5.webp +0 -0
- torre/ui/art/bots/ukraine-6.webp +0 -0
- torre/ui/art/bots/ukraine-7.webp +0 -0
- torre/ui/art/bots/ukraine-8.webp +0 -0
- torre/ui/art/bots/usa-1.webp +0 -0
- torre/ui/art/bots/usa-2.webp +0 -0
- torre/ui/art/bots/usa-3.webp +0 -0
- torre/ui/art/bots/usa-4.webp +0 -0
- torre/ui/art/bots/usa-5.webp +0 -0
- torre/ui/art/bots/usa-6.webp +0 -0
- torre/ui/art/bots/usa-7.webp +0 -0
- torre/ui/art/bots/usa-8.webp +0 -0
- torre/ui/art/bots/venezuela-1-1.webp +0 -0
- torre/ui/art/bots/venezuela-1-2.webp +0 -0
- torre/ui/art/bots/venezuela-1-3.webp +0 -0
- torre/ui/art/bots/venezuela-1-4.webp +0 -0
- torre/ui/art/bots/venezuela-1-5.webp +0 -0
- torre/ui/art/bots/venezuela-1-6.webp +0 -0
- torre/ui/art/bots/venezuela-1-7.webp +0 -0
- torre/ui/art/bots/venezuela-1-8.webp +0 -0
- torre/ui/art/bots/venezuela-2-1.webp +0 -0
- torre/ui/art/bots/venezuela-2-2.webp +0 -0
- torre/ui/art/bots/venezuela-2-3.webp +0 -0
- torre/ui/art/bots/venezuela-2-4.webp +0 -0
- torre/ui/art/bots/venezuela-2-5.webp +0 -0
- torre/ui/art/bots/venezuela-2-6.webp +0 -0
- torre/ui/art/bots/venezuela-2-7.webp +0 -0
- torre/ui/art/bots/venezuela-2-8.webp +0 -0
- torre/ui/art/engines/leela.webp +0 -0
- torre/ui/art/engines/maia.webp +0 -0
- torre/ui/art/engines/random.webp +0 -0
- torre/ui/art/engines/sf11.webp +0 -0
- torre/ui/art/engines/sf18.webp +0 -0
- torre/ui/art/grades/best.webp +0 -0
- torre/ui/art/grades/blunder.webp +0 -0
- torre/ui/art/grades/brilliant.webp +0 -0
- torre/ui/art/grades/good.webp +0 -0
- torre/ui/art/grades/icon/best.webp +0 -0
- torre/ui/art/grades/icon/blunder.webp +0 -0
- torre/ui/art/grades/icon/book.webp +0 -0
- torre/ui/art/grades/icon/brilliant.webp +0 -0
- torre/ui/art/grades/icon/good.webp +0 -0
- torre/ui/art/grades/icon/great.webp +0 -0
- torre/ui/art/grades/icon/inaccuracy.webp +0 -0
- torre/ui/art/grades/icon/mistake.webp +0 -0
- torre/ui/art/grades/inaccuracy.webp +0 -0
- torre/ui/art/grades/mistake.webp +0 -0
- torre/ui/art/history/alekhine.webp +0 -0
- torre/ui/art/history/anand.webp +0 -0
- torre/ui/art/history/botvinnik.webp +0 -0
- torre/ui/art/history/capablanca.webp +0 -0
- torre/ui/art/history/carlsen.webp +0 -0
- torre/ui/art/history/engine-era.webp +0 -0
- torre/ui/art/history/evergreen-game.webp +0 -0
- torre/ui/art/history/fischer.webp +0 -0
- torre/ui/art/history/game-of-the-century.webp +0 -0
- torre/ui/art/history/hypermodern.webp +0 -0
- torre/ui/art/history/immortal-game.webp +0 -0
- torre/ui/art/history/karpov.webp +0 -0
- torre/ui/art/history/kasparov.webp +0 -0
- torre/ui/art/history/kramnik.webp +0 -0
- torre/ui/art/history/lasker.webp +0 -0
- torre/ui/art/history/morphy.webp +0 -0
- torre/ui/art/history/petrosian.webp +0 -0
- torre/ui/art/history/polgar.webp +0 -0
- torre/ui/art/history/romantic-era.webp +0 -0
- torre/ui/art/history/soviet-school.webp +0 -0
- torre/ui/art/history/spassky.webp +0 -0
- torre/ui/art/history/steinitz.webp +0 -0
- torre/ui/art/history/tal.webp +0 -0
- torre/ui/art/history/the-turk.webp +0 -0
- torre/ui/art/home/balloon.webp +0 -0
- torre/ui/art/home/bishop-telescope.webp +0 -0
- torre/ui/art/home/bots.webp +0 -0
- torre/ui/art/home/break.webp +0 -0
- torre/ui/art/home/butterflies.webp +0 -0
- torre/ui/art/home/campfire.webp +0 -0
- torre/ui/art/home/cloud-long.webp +0 -0
- torre/ui/art/home/cloud-puff.webp +0 -0
- torre/ui/art/home/islet-tree.webp +0 -0
- torre/ui/art/home/islet-waterfall.webp +0 -0
- torre/ui/art/home/journey.webp +0 -0
- torre/ui/art/home/king-bush.webp +0 -0
- torre/ui/art/home/knight-sneak.webp +0 -0
- torre/ui/art/home/leapfrog.webp +0 -0
- torre/ui/art/home/leaves.webp +0 -0
- torre/ui/art/home/moon.webp +0 -0
- torre/ui/art/home/paper-plane.webp +0 -0
- torre/ui/art/home/pawn-cloud.webp +0 -0
- torre/ui/art/home/picnic.webp +0 -0
- torre/ui/art/home/puzzles.webp +0 -0
- torre/ui/art/home/queen-reading.webp +0 -0
- torre/ui/art/home/robot.webp +0 -0
- torre/ui/art/home/rook-nap.webp +0 -0
- torre/ui/art/home/sun.webp +0 -0
- torre/ui/art/home/train.webp +0 -0
- torre/ui/art/home/tumbling.webp +0 -0
- torre/ui/art/icons/hint.png +0 -0
- torre/ui/art/icons/resign.png +0 -0
- torre/ui/art/legends/Alekhine.webp +0 -0
- torre/ui/art/legends/Anand.webp +0 -0
- torre/ui/art/legends/Anderssen.webp +0 -0
- torre/ui/art/legends/Aronian.webp +0 -0
- torre/ui/art/legends/Botvinnik.webp +0 -0
- torre/ui/art/legends/Bronstein.webp +0 -0
- torre/ui/art/legends/Capablanca.webp +0 -0
- torre/ui/art/legends/Carlsen.webp +0 -0
- torre/ui/art/legends/Caruana.webp +0 -0
- torre/ui/art/legends/Chiburdanidze.webp +0 -0
- torre/ui/art/legends/Chigorin.webp +0 -0
- torre/ui/art/legends/DeLaBourdonnais.webp +0 -0
- torre/ui/art/legends/Ding.webp +0 -0
- torre/ui/art/legends/Euwe.webp +0 -0
- torre/ui/art/legends/Firouzja.webp +0 -0
- torre/ui/art/legends/Fischer.webp +0 -0
- torre/ui/art/legends/Gaprindashvili.webp +0 -0
- torre/ui/art/legends/Gukesh.webp +0 -0
- torre/ui/art/legends/Hou.webp +0 -0
- torre/ui/art/legends/JuWenjun.webp +0 -0
- torre/ui/art/legends/Karpov.webp +0 -0
- torre/ui/art/legends/Kasparov.webp +0 -0
- torre/ui/art/legends/Keres.webp +0 -0
- torre/ui/art/legends/Korchnoi.webp +0 -0
- torre/ui/art/legends/Kramnik.webp +0 -0
- torre/ui/art/legends/Larsen.webp +0 -0
- torre/ui/art/legends/Lasker.webp +0 -0
- torre/ui/art/legends/Marshall.webp +0 -0
- torre/ui/art/legends/Menchik.webp +0 -0
- torre/ui/art/legends/Morphy.webp +0 -0
- torre/ui/art/legends/Najdorf.webp +0 -0
- torre/ui/art/legends/Nakamura.webp +0 -0
- torre/ui/art/legends/Nepomniachtchi.webp +0 -0
- torre/ui/art/legends/Nimzowitsch.webp +0 -0
- torre/ui/art/legends/Petrosian.webp +0 -0
- torre/ui/art/legends/Philidor.webp +0 -0
- torre/ui/art/legends/Pillsbury.webp +0 -0
- torre/ui/art/legends/PolgarJ.webp +0 -0
- torre/ui/art/legends/PolgarS.webp +0 -0
- torre/ui/art/legends/Reti.webp +0 -0
- torre/ui/art/legends/Rubinstein.webp +0 -0
- torre/ui/art/legends/Smyslov.webp +0 -0
- torre/ui/art/legends/So.webp +0 -0
- torre/ui/art/legends/Spassky.webp +0 -0
- torre/ui/art/legends/Steinitz.webp +0 -0
- torre/ui/art/legends/Tal.webp +0 -0
- torre/ui/art/legends/Tartakower.webp +0 -0
- torre/ui/art/legends/Topalov.webp +0 -0
- torre/ui/art/legends/era-cold-war.webp +0 -0
- torre/ui/art/legends/era-golden.webp +0 -0
- torre/ui/art/legends/era-modern.webp +0 -0
- torre/ui/art/legends/era-romantic.webp +0 -0
- torre/ui/art/legends/hall-banner.webp +0 -0
- torre/ui/art/legends/hall-cloud.webp +0 -0
- torre/ui/art/legends/hall-pedestal.webp +0 -0
- torre/ui/art/legends/hall-swords.webp +0 -0
- torre/ui/art/legends/hall-wreath.webp +0 -0
- torre/ui/art/logo/app-icon-192.png +0 -0
- torre/ui/art/logo/app-icon-32.png +0 -0
- torre/ui/art/logo/app-icon-512.png +0 -0
- torre/ui/art/logo/app-icon-64.png +0 -0
- torre/ui/art/logo/mark.png +0 -0
- torre/ui/art/mascot/alarm.webp +0 -0
- torre/ui/art/mascot/bored.webp +0 -0
- torre/ui/art/mascot/calculating.webp +0 -0
- torre/ui/art/mascot/check.webp +0 -0
- torre/ui/art/mascot/cheer.webp +0 -0
- torre/ui/art/mascot/clap.webp +0 -0
- torre/ui/art/mascot/confetti.webp +0 -0
- torre/ui/art/mascot/confused.webp +0 -0
- torre/ui/art/mascot/content.webp +0 -0
- torre/ui/art/mascot/cursor/body-arms-up.webp +0 -0
- torre/ui/art/mascot/cursor/body-crossed.webp +0 -0
- torre/ui/art/mascot/cursor/body-hips.webp +0 -0
- torre/ui/art/mascot/cursor/body-reach.webp +0 -0
- torre/ui/art/mascot/cursor/body.webp +0 -0
- torre/ui/art/mascot/cursor/boop.webp +0 -0
- torre/ui/art/mascot/cursor/bye.webp +0 -0
- torre/ui/art/mascot/cursor/dizzy.webp +0 -0
- torre/ui/art/mascot/cursor/doze.webp +0 -0
- torre/ui/art/mascot/cursor/hello.webp +0 -0
- torre/ui/art/mascot/cursor/startled.webp +0 -0
- torre/ui/art/mascot/cursor/tickled.webp +0 -0
- torre/ui/art/mascot/cursor/wake.webp +0 -0
- torre/ui/art/mascot/dancing.webp +0 -0
- torre/ui/art/mascot/detective.webp +0 -0
- torre/ui/art/mascot/determined.webp +0 -0
- torre/ui/art/mascot/dizzy.webp +0 -0
- torre/ui/art/mascot/excited.webp +0 -0
- torre/ui/art/mascot/facepalm.webp +0 -0
- torre/ui/art/mascot/gift.webp +0 -0
- torre/ui/art/mascot/happy.webp +0 -0
- torre/ui/art/mascot/host.webp +0 -0
- torre/ui/art/mascot/hug.webp +0 -0
- torre/ui/art/mascot/idea.webp +0 -0
- torre/ui/art/mascot/magnify.webp +0 -0
- torre/ui/art/mascot/move-piece.webp +0 -0
- torre/ui/art/mascot/nervous.webp +0 -0
- torre/ui/art/mascot/neutral.webp +0 -0
- torre/ui/art/mascot/no.webp +0 -0
- torre/ui/art/mascot/nod.webp +0 -0
- torre/ui/art/mascot/notes.webp +0 -0
- torre/ui/art/mascot/panic.webp +0 -0
- torre/ui/art/mascot/playing.webp +0 -0
- torre/ui/art/mascot/point-board.webp +0 -0
- torre/ui/art/mascot/point.webp +0 -0
- torre/ui/art/mascot/pointer.webp +0 -0
- torre/ui/art/mascot/present.webp +0 -0
- torre/ui/art/mascot/proud.webp +0 -0
- torre/ui/art/mascot/puzzle.webp +0 -0
- torre/ui/art/mascot/ready.webp +0 -0
- torre/ui/art/mascot/relieved.webp +0 -0
- torre/ui/art/mascot/rival.webp +0 -0
- torre/ui/art/mascot/sad.webp +0 -0
- torre/ui/art/mascot/salute.webp +0 -0
- torre/ui/art/mascot/sheepish.webp +0 -0
- torre/ui/art/mascot/shield.webp +0 -0
- torre/ui/art/mascot/shocked.webp +0 -0
- torre/ui/art/mascot/shrug.webp +0 -0
- torre/ui/art/mascot/sleepy.webp +0 -0
- torre/ui/art/mascot/star.webp +0 -0
- torre/ui/art/mascot/starry.webp +0 -0
- torre/ui/art/mascot/study.webp +0 -0
- torre/ui/art/mascot/teary.webp +0 -0
- torre/ui/art/mascot/thinking.webp +0 -0
- torre/ui/art/mascot/thumbs-up.webp +0 -0
- torre/ui/art/mascot/trophy.webp +0 -0
- torre/ui/art/mascot/warn.webp +0 -0
- torre/ui/art/mascot/wave.webp +0 -0
- torre/ui/art/mascot/wink.webp +0 -0
- torre/ui/art/mascot/yawn.webp +0 -0
- torre/ui/art/nav/awards.png +0 -0
- torre/ui/art/nav/explore.png +0 -0
- torre/ui/art/nav/games.png +0 -0
- torre/ui/art/nav/home.png +0 -0
- torre/ui/art/nav/lessons.png +0 -0
- torre/ui/art/nav/play.png +0 -0
- torre/ui/art/nav/puzzles.png +0 -0
- torre/ui/art/nav/train.png +0 -0
- torre/ui/art/openings/bird.webp +0 -0
- torre/ui/art/openings/caro-kann.webp +0 -0
- torre/ui/art/openings/colle.webp +0 -0
- torre/ui/art/openings/dutch.webp +0 -0
- torre/ui/art/openings/english.webp +0 -0
- torre/ui/art/openings/french.webp +0 -0
- torre/ui/art/openings/italian.webp +0 -0
- torre/ui/art/openings/kings-gambit.webp +0 -0
- torre/ui/art/openings/kings-indian.webp +0 -0
- torre/ui/art/openings/kings-knight.webp +0 -0
- torre/ui/art/openings/kings-pawn.webp +0 -0
- torre/ui/art/openings/london.webp +0 -0
- torre/ui/art/openings/philidor.webp +0 -0
- torre/ui/art/openings/pirc.webp +0 -0
- torre/ui/art/openings/queens-gambit.webp +0 -0
- torre/ui/art/openings/queens-pawn.webp +0 -0
- torre/ui/art/openings/ruy-lopez.webp +0 -0
- torre/ui/art/openings/scandinavian.webp +0 -0
- torre/ui/art/openings/scotch.webp +0 -0
- torre/ui/art/openings/sicilian.webp +0 -0
- torre/ui/art/openings/slav.webp +0 -0
- torre/ui/art/openings/trompowsky.webp +0 -0
- torre/ui/art/openings/uncommon.webp +0 -0
- torre/ui/art/openings/vienna.webp +0 -0
- torre/ui/art/pieces/black-bishop.webp +0 -0
- torre/ui/art/pieces/black-king.webp +0 -0
- torre/ui/art/pieces/black-knight.webp +0 -0
- torre/ui/art/pieces/black-pawn.webp +0 -0
- torre/ui/art/pieces/black-queen.webp +0 -0
- torre/ui/art/pieces/black-resigned.webp +0 -0
- torre/ui/art/pieces/black-rook.webp +0 -0
- torre/ui/art/pieces/black-winner.webp +0 -0
- torre/ui/art/pieces/board/bB.webp +0 -0
- torre/ui/art/pieces/board/bK.webp +0 -0
- torre/ui/art/pieces/board/bN.webp +0 -0
- torre/ui/art/pieces/board/bP.webp +0 -0
- torre/ui/art/pieces/board/bQ.webp +0 -0
- torre/ui/art/pieces/board/bR.webp +0 -0
- torre/ui/art/pieces/board/wB.webp +0 -0
- torre/ui/art/pieces/board/wK.webp +0 -0
- torre/ui/art/pieces/board/wN.webp +0 -0
- torre/ui/art/pieces/board/wP.webp +0 -0
- torre/ui/art/pieces/board/wQ.webp +0 -0
- torre/ui/art/pieces/board/wR.webp +0 -0
- torre/ui/art/pieces/staunton/bB.webp +0 -0
- torre/ui/art/pieces/staunton/bK.webp +0 -0
- torre/ui/art/pieces/staunton/bN.webp +0 -0
- torre/ui/art/pieces/staunton/bP.webp +0 -0
- torre/ui/art/pieces/staunton/bQ.webp +0 -0
- torre/ui/art/pieces/staunton/bR.webp +0 -0
- torre/ui/art/pieces/staunton/wB.webp +0 -0
- torre/ui/art/pieces/staunton/wK.webp +0 -0
- torre/ui/art/pieces/staunton/wN.webp +0 -0
- torre/ui/art/pieces/staunton/wP.webp +0 -0
- torre/ui/art/pieces/staunton/wQ.webp +0 -0
- torre/ui/art/pieces/staunton/wR.webp +0 -0
- torre/ui/art/pieces/white-bishop.webp +0 -0
- torre/ui/art/pieces/white-king.webp +0 -0
- torre/ui/art/pieces/white-knight.webp +0 -0
- torre/ui/art/pieces/white-pawn.webp +0 -0
- torre/ui/art/pieces/white-queen.webp +0 -0
- torre/ui/art/pieces/white-resigned.webp +0 -0
- torre/ui/art/pieces/white-rook.webp +0 -0
- torre/ui/art/pieces/white-winner.webp +0 -0
- torre/ui/art/progress/calendar.webp +0 -0
- torre/ui/art/progress/clock.webp +0 -0
- torre/ui/art/progress/growth.webp +0 -0
- torre/ui/art/progress/study.webp +0 -0
- torre/ui/art/progress/target.webp +0 -0
- torre/ui/art/progress/victory.webp +0 -0
- torre/ui/art/robots/robot-1.webp +0 -0
- torre/ui/art/robots/robot-2.webp +0 -0
- torre/ui/art/robots/robot-3.webp +0 -0
- torre/ui/art/robots/robot-4.webp +0 -0
- torre/ui/art/robots/robot-5.webp +0 -0
- torre/ui/art/robots/robot-6.webp +0 -0
- torre/ui/art/robots/robot-7.webp +0 -0
- torre/ui/art/robots/robot-8.webp +0 -0
- torre/ui/art/stars/star-empty.png +0 -0
- torre/ui/art/stars/star-gold.png +0 -0
- torre/ui/art/story/basics/boulder.webp +0 -0
- torre/ui/art/story/basics/bridge.webp +0 -0
- torre/ui/art/story/basics/castle.webp +0 -0
- torre/ui/art/story/basics/edge-1.webp +0 -0
- torre/ui/art/story/basics/fence.webp +0 -0
- torre/ui/art/story/basics/flowers.webp +0 -0
- torre/ui/art/story/basics/mushrooms.webp +0 -0
- torre/ui/art/story/basics/pine.webp +0 -0
- torre/ui/art/story/basics/tree.webp +0 -0
- torre/ui/art/story/birds.webp +0 -0
- torre/ui/art/story/boss-gate.webp +0 -0
- torre/ui/art/story/chapter/complete.webp +0 -0
- torre/ui/art/story/chapter/first-moves.webp +0 -0
- torre/ui/art/story/chapter/opening-ideas.webp +0 -0
- torre/ui/art/story/chapter/the-immortals.webp +0 -0
- torre/ui/art/story/chapter/the-last-twenty-moves.webp +0 -0
- torre/ui/art/story/chapter/the-mating-net.webp +0 -0
- torre/ui/art/story/chapter/the-quiet-board.webp +0 -0
- torre/ui/art/story/chapter/tricks-of-the-trade.webp +0 -0
- torre/ui/art/story/checkmates/dead-tree.webp +0 -0
- torre/ui/art/story/checkmates/edge-1.webp +0 -0
- torre/ui/art/story/checkmates/edge-2.webp +0 -0
- torre/ui/art/story/checkmates/ferns.webp +0 -0
- torre/ui/art/story/checkmates/lantern.webp +0 -0
- torre/ui/art/story/checkmates/log.webp +0 -0
- torre/ui/art/story/checkmates/net.webp +0 -0
- torre/ui/art/story/checkmates/pine.webp +0 -0
- torre/ui/art/story/checkmates/tower.webp +0 -0
- torre/ui/art/story/checkmates/web.webp +0 -0
- torre/ui/art/story/chest.webp +0 -0
- torre/ui/art/story/cloud.webp +0 -0
- torre/ui/art/story/endgames/bush.webp +0 -0
- torre/ui/art/story/endgames/cabin.webp +0 -0
- torre/ui/art/story/endgames/campfire.webp +0 -0
- torre/ui/art/story/endgames/edge-1.webp +0 -0
- torre/ui/art/story/endgames/edge-2.webp +0 -0
- torre/ui/art/story/endgames/ice-rock.webp +0 -0
- torre/ui/art/story/endgames/peak.webp +0 -0
- torre/ui/art/story/endgames/pine.webp +0 -0
- torre/ui/art/story/endgames/sled.webp +0 -0
- torre/ui/art/story/endgames/snowman.webp +0 -0
- torre/ui/art/story/gauntlet.webp +0 -0
- torre/ui/art/story/hero-cheer.webp +0 -0
- torre/ui/art/story/hero-flag.webp +0 -0
- torre/ui/art/story/history/amphora.webp +0 -0
- torre/ui/art/story/history/archway.webp +0 -0
- torre/ui/art/story/history/column.webp +0 -0
- torre/ui/art/story/history/cypress.webp +0 -0
- torre/ui/art/story/history/edge-1.webp +0 -0
- torre/ui/art/story/history/edge-2.webp +0 -0
- torre/ui/art/story/history/laurel.webp +0 -0
- torre/ui/art/story/history/rubble.webp +0 -0
- torre/ui/art/story/history/tablet.webp +0 -0
- torre/ui/art/story/history/temple.webp +0 -0
- torre/ui/art/story/openings/barrels.webp +0 -0
- torre/ui/art/story/openings/cottage.webp +0 -0
- torre/ui/art/story/openings/edge-1.webp +0 -0
- torre/ui/art/story/openings/edge-2.webp +0 -0
- torre/ui/art/story/openings/haystack.webp +0 -0
- torre/ui/art/story/openings/signpost.webp +0 -0
- torre/ui/art/story/openings/sunflowers.webp +0 -0
- torre/ui/art/story/openings/tree.webp +0 -0
- torre/ui/art/story/openings/wheelbarrow.webp +0 -0
- torre/ui/art/story/openings/windmill.webp +0 -0
- torre/ui/art/story/quote-hill.webp +0 -0
- torre/ui/art/story/strategy/bamboo.webp +0 -0
- torre/ui/art/story/strategy/blossom.webp +0 -0
- torre/ui/art/story/strategy/bonsai.webp +0 -0
- torre/ui/art/story/strategy/edge-1.webp +0 -0
- torre/ui/art/story/strategy/edge-2.webp +0 -0
- torre/ui/art/story/strategy/lantern.webp +0 -0
- torre/ui/art/story/strategy/pagoda.webp +0 -0
- torre/ui/art/story/strategy/pond.webp +0 -0
- torre/ui/art/story/strategy/red-bridge.webp +0 -0
- torre/ui/art/story/strategy/sand.webp +0 -0
- torre/ui/art/story/tactics/arch.webp +0 -0
- torre/ui/art/story/tactics/bush.webp +0 -0
- torre/ui/art/story/tactics/cactus.webp +0 -0
- torre/ui/art/story/tactics/crate.webp +0 -0
- torre/ui/art/story/tactics/edge-1.webp +0 -0
- torre/ui/art/story/tactics/edge-2.webp +0 -0
- torre/ui/art/story/tactics/mesa.webp +0 -0
- torre/ui/art/story/tactics/rope-bridge.webp +0 -0
- torre/ui/art/story/tactics/spire.webp +0 -0
- torre/ui/art/story/tactics/tumbleweed.webp +0 -0
- torre/ui/art/topics/analysis.webp +0 -0
- torre/ui/art/topics/anastasia-mate.webp +0 -0
- torre/ui/art/topics/arabian-mate.webp +0 -0
- torre/ui/art/topics/as-black.webp +0 -0
- torre/ui/art/topics/as-white.webp +0 -0
- torre/ui/art/topics/attraction.webp +0 -0
- torre/ui/art/topics/bishop-endgame.webp +0 -0
- torre/ui/art/topics/boden-mate.webp +0 -0
- torre/ui/art/topics/bots.webp +0 -0
- torre/ui/art/topics/bridge.webp +0 -0
- torre/ui/art/topics/capture-defender.webp +0 -0
- torre/ui/art/topics/defensive-move.webp +0 -0
- torre/ui/art/topics/double-check.webp +0 -0
- torre/ui/art/topics/endgame.webp +0 -0
- torre/ui/art/topics/explore.webp +0 -0
- torre/ui/art/topics/exposed-king.webp +0 -0
- torre/ui/art/topics/form.webp +0 -0
- torre/ui/art/topics/games.webp +0 -0
- torre/ui/art/topics/hanging-piece.webp +0 -0
- torre/ui/art/topics/hook-mate.webp +0 -0
- torre/ui/art/topics/import.webp +0 -0
- torre/ui/art/topics/insights.webp +0 -0
- torre/ui/art/topics/kingside-attack.webp +0 -0
- torre/ui/art/topics/knight-endgame.webp +0 -0
- torre/ui/art/topics/mate-in-1.webp +0 -0
- torre/ui/art/topics/mate-in-2.webp +0 -0
- torre/ui/art/topics/mate-in-3.webp +0 -0
- torre/ui/art/topics/middlegame.webp +0 -0
- torre/ui/art/topics/mistakes.webp +0 -0
- torre/ui/art/topics/new-game.webp +0 -0
- torre/ui/art/topics/opening.webp +0 -0
- torre/ui/art/topics/opposition.webp +0 -0
- torre/ui/art/topics/pawn-endgame.webp +0 -0
- torre/ui/art/topics/pawn-push.webp +0 -0
- torre/ui/art/topics/queen-endgame.webp +0 -0
- torre/ui/art/topics/queen-mate.webp +0 -0
- torre/ui/art/topics/quiet-move.webp +0 -0
- torre/ui/art/topics/rating.webp +0 -0
- torre/ui/art/topics/recurring.webp +0 -0
- torre/ui/art/topics/retry.webp +0 -0
- torre/ui/art/topics/rook-endgame.webp +0 -0
- torre/ui/art/topics/smothered-mate.webp +0 -0
- torre/ui/art/topics/train.webp +0 -0
- torre/ui/art/topics/trapped-piece.webp +0 -0
- torre/ui/art/topics/underpromotion.webp +0 -0
- torre/ui/art/topics/zwischenzug.webp +0 -0
- torre/ui/art/ui3d/achievement.webp +0 -0
- torre/ui/art/ui3d/defeat.webp +0 -0
- torre/ui/art/ui3d/help.webp +0 -0
- torre/ui/art/ui3d/info.webp +0 -0
- torre/ui/art/ui3d/level-complete.webp +0 -0
- torre/ui/art/ui3d/locked.webp +0 -0
- torre/ui/art/ui3d/mute.webp +0 -0
- torre/ui/art/ui3d/pause.webp +0 -0
- torre/ui/art/ui3d/play.webp +0 -0
- torre/ui/art/ui3d/retry.webp +0 -0
- torre/ui/art/ui3d/reward.webp +0 -0
- torre/ui/art/ui3d/settings.webp +0 -0
- torre/ui/art/ui3d/sound.webp +0 -0
- torre/ui/art/ui3d/time.webp +0 -0
- torre/ui/art/ui3d/unlocked.webp +0 -0
- torre/ui/art/ui3d/victory.webp +0 -0
- torre/ui/art/video/figure-alekhine-attack.webp +0 -0
- torre/ui/art/video/figure-anderssen-evergreen.webp +0 -0
- torre/ui/art/video/figure-anderssen-immortal.webp +0 -0
- torre/ui/art/video/figure-botvinnik-worried.webp +0 -0
- torre/ui/art/video/figure-byrne.webp +0 -0
- torre/ui/art/video/figure-capablanca-qgd.webp +0 -0
- torre/ui/art/video/figure-dufresne.webp +0 -0
- torre/ui/art/video/figure-duke-and-count.webp +0 -0
- torre/ui/art/video/figure-fischer-alone.webp +0 -0
- torre/ui/art/video/figure-fischer-c4.webp +0 -0
- torre/ui/art/video/figure-fischer-champion.webp +0 -0
- torre/ui/art/video/figure-fischer-demands.webp +0 -0
- torre/ui/art/video/figure-fischer-focused.webp +0 -0
- torre/ui/art/video/figure-fischer-sacrifice.webp +0 -0
- torre/ui/art/video/figure-fischer-young.webp +0 -0
- torre/ui/art/video/figure-gurgenidze.webp +0 -0
- torre/ui/art/video/figure-hecht.webp +0 -0
- torre/ui/art/video/figure-karpov-rival.webp +0 -0
- torre/ui/art/video/figure-kasparov-champion.webp +0 -0
- torre/ui/art/video/figure-kasparov-deep-blue.webp +0 -0
- torre/ui/art/video/figure-kasparov-fierce.webp +0 -0
- torre/ui/art/video/figure-kasparov-qg.webp +0 -0
- torre/ui/art/video/figure-kasparov-sacrifice.webp +0 -0
- torre/ui/art/video/figure-kasparov-young.webp +0 -0
- torre/ui/art/video/figure-kieseritzky-shocked.webp +0 -0
- torre/ui/art/video/figure-larsen-bold.webp +0 -0
- torre/ui/art/video/figure-morphy-blindfold.webp +0 -0
- torre/ui/art/video/figure-morphy-opera.webp +0 -0
- torre/ui/art/video/figure-morphy-young.webp +0 -0
- torre/ui/art/video/figure-najdorf-resigns.webp +0 -0
- torre/ui/art/video/figure-portisch-pained.webp +0 -0
- torre/ui/art/video/figure-portisch.webp +0 -0
- torre/ui/art/video/figure-rook-books.webp +0 -0
- torre/ui/art/video/figure-rook-cheers.webp +0 -0
- torre/ui/art/video/figure-rook-gift.webp +0 -0
- torre/ui/art/video/figure-rook-points-d4.webp +0 -0
- torre/ui/art/video/figure-rook-presents.webp +0 -0
- torre/ui/art/video/figure-rook-warns.webp +0 -0
- torre/ui/art/video/figure-rubinstein-study.webp +0 -0
- torre/ui/art/video/figure-smyslov-shocked.webp +0 -0
- torre/ui/art/video/figure-spassky-applauds.webp +0 -0
- torre/ui/art/video/figure-spassky-calm.webp +0 -0
- torre/ui/art/video/figure-tal-champion.webp +0 -0
- torre/ui/art/video/figure-tal-grin.webp +0 -0
- torre/ui/art/video/figure-tal-magic.webp +0 -0
- torre/ui/art/video/figure-tal-sacrifice.webp +0 -0
- torre/ui/art/video/figure-tal-thinking.webp +0 -0
- torre/ui/art/video/figure-tal-young.webp +0 -0
- torre/ui/art/video/moment-alarm-king.webp +0 -0
- torre/ui/art/video/moment-brilliant-gem.webp +0 -0
- torre/ui/art/video/moment-candidates-cup.webp +0 -0
- torre/ui/art/video/moment-checkmate-net.webp +0 -0
- torre/ui/art/video/moment-chess-clock.webp +0 -0
- torre/ui/art/video/moment-exploding-board.webp +0 -0
- torre/ui/art/video/moment-fire-rook.webp +0 -0
- torre/ui/art/video/moment-hanging-bishop.webp +0 -0
- torre/ui/art/video/moment-lightning-knight.webp +0 -0
- torre/ui/art/video/moment-newspaper.webp +0 -0
- torre/ui/art/video/moment-only-move.webp +0 -0
- torre/ui/art/video/moment-qg-accepted-hug.webp +0 -0
- torre/ui/art/video/moment-qg-cant-hold.webp +0 -0
- torre/ui/art/video/moment-qg-center.webp +0 -0
- torre/ui/art/video/moment-qg-crowned-pawn.webp +0 -0
- torre/ui/art/video/moment-qg-declined-wall.webp +0 -0
- torre/ui/art/video/moment-qg-duel.webp +0 -0
- torre/ui/art/video/moment-qg-elephant-trap.webp +0 -0
- torre/ui/art/video/moment-qg-gift-queen.webp +0 -0
- torre/ui/art/video/moment-qg-greedy-pawn.webp +0 -0
- torre/ui/art/video/moment-qg-isolated-pawn.webp +0 -0
- torre/ui/art/video/moment-qg-lasker-trap.webp +0 -0
- torre/ui/art/video/moment-qg-minority-attack.webp +0 -0
- torre/ui/art/video/moment-qg-offer.webp +0 -0
- torre/ui/art/video/moment-qg-pin.webp +0 -0
- torre/ui/art/video/moment-qg-slav-shield.webp +0 -0
- torre/ui/art/video/moment-qg-tempted-pawn.webp +0 -0
- torre/ui/art/video/moment-qg-trapdoor.webp +0 -0
- torre/ui/art/video/moment-qg-two-doors.webp +0 -0
- torre/ui/art/video/moment-queen-sacrifice.webp +0 -0
- torre/ui/art/video/moment-resign.webp +0 -0
- torre/ui/art/video/moment-scoresheet.webp +0 -0
- torre/ui/art/video/moment-shocked-queen.webp +0 -0
- torre/ui/art/video/moment-spotlight-board.webp +0 -0
- torre/ui/art/video/moment-trapped-queen.webp +0 -0
- torre/ui/art/video/moment-world-crown.webp +0 -0
- torre/ui/art/video/venue-berlin-1852.webp +0 -0
- torre/ui/art/video/venue-bled.webp +0 -0
- torre/ui/art/video/venue-leipzig.webp +0 -0
- torre/ui/art/video/venue-london-1851.webp +0 -0
- torre/ui/art/video/venue-moscow-1985.webp +0 -0
- torre/ui/art/video/venue-moscow-match.webp +0 -0
- torre/ui/art/video/venue-new-york-1956.webp +0 -0
- torre/ui/art/video/venue-new-york-1997.webp +0 -0
- torre/ui/art/video/venue-paris-opera-1858.webp +0 -0
- torre/ui/art/video/venue-reykjavik-1972.webp +0 -0
- torre/ui/art/video/venue-reykjavik.webp +0 -0
- torre/ui/art/video/venue-riga.webp +0 -0
- torre/ui/art/video/venue-tilburg.webp +0 -0
- torre/ui/art/video/venue-varna.webp +0 -0
- torre/ui/art/video/venue-wijk-aan-zee-1999.webp +0 -0
- torre/ui/art/video/venue-zurich.webp +0 -0
- torre/ui/art/yours/endgame-grinder.webp +0 -0
- torre/ui/art/yours/opening-tourist.webp +0 -0
- torre/ui/art/yours/positional-master.webp +0 -0
- torre/ui/art/yours/present-you.webp +0 -0
- torre/ui/art/yours/reckless-attacker.webp +0 -0
- torre/ui/art/yours/tactical-sharpie.webp +0 -0
- torre/ui/art/yours/the-trader.webp +0 -0
- torre/ui/art/yours/train.webp +0 -0
- torre/ui/assets/AiSetup-D0ojvOo6.js +1 -0
- torre/ui/assets/AnalysisBanner-CgzZjuOs.js +1 -0
- torre/ui/assets/ArenaPage-DhhtFsO6.js +1 -0
- torre/ui/assets/AwardsPage-DIBR--pI.js +1 -0
- torre/ui/assets/BoardWorkspace-Cxrtuejv.js +1 -0
- torre/ui/assets/BotCollectionPage-DMIe2Mqp.js +1 -0
- torre/ui/assets/BotReport-CegOQG0r.js +1 -0
- torre/ui/assets/BotsHomePage-D73LJY3O.js +1 -0
- torre/ui/assets/CoachExplain-CfTPObIL.js +1 -0
- torre/ui/assets/CoachPage-D01hygof.js +1 -0
- torre/ui/assets/CursorRook-B7eCvqMP.js +1 -0
- torre/ui/assets/EndgamesPage-DJGzTpkE.js +1 -0
- torre/ui/assets/ExplorePage-iB6Kc6kX.js +1 -0
- torre/ui/assets/GamesPage-BeCyNtCU.js +1 -0
- torre/ui/assets/HomePage-jcAUG1nj.js +1 -0
- torre/ui/assets/ImportPage-Bn5g3rvQ.js +4 -0
- torre/ui/assets/InsightsPage-CHoneM_p.js +1 -0
- torre/ui/assets/LessonsPage-DeE2ulM2.js +1 -0
- torre/ui/assets/MoveList-DytFbAp6.js +1 -0
- torre/ui/assets/NavIcon-Cdek5ONP.js +1 -0
- torre/ui/assets/OpeningStory-CnmDg1zw.js +1 -0
- torre/ui/assets/OpeningsPage-DydTgh8d.js +1 -0
- torre/ui/assets/PlayPage-DK9-UawI.js +3 -0
- torre/ui/assets/ProfilePage-DOSgcVMv.js +1 -0
- torre/ui/assets/PuzzlesPage-ofZhK6FU.js +1 -0
- torre/ui/assets/RookSpeaker-B_HLzkJm.js +2 -0
- torre/ui/assets/Segments-B8XJlwPB.js +1 -0
- torre/ui/assets/SettingsPage-C4mt0o7O.js +1 -0
- torre/ui/assets/Sidebar-Cio69IXU.js +1 -0
- torre/ui/assets/StagePage-CMmIR1l-.js +1 -0
- torre/ui/assets/StoryPage-DplmoBTf.js +1 -0
- torre/ui/assets/Streak-Co_cfm0s.js +1 -0
- torre/ui/assets/TodayPlan-D_0arCyI.js +1 -0
- torre/ui/assets/TrainPage-D5NthACf.js +1 -0
- torre/ui/assets/TrainerPages-BHIhhs8R.js +1 -0
- torre/ui/assets/awards-B1W0K4TV.js +1 -0
- torre/ui/assets/bots-CqReul03.js +1 -0
- torre/ui/assets/collections-2qe_ozuB.js +1 -0
- torre/ui/assets/content-D7CTF3q9.js +1 -0
- torre/ui/assets/content-en-CcyMO59M.js +1 -0
- torre/ui/assets/content-es-CvyOw0FZ.js +1 -0
- torre/ui/assets/events-ClR1xEWT.js +1 -0
- torre/ui/assets/format-BR0clSXu.js +1 -0
- torre/ui/assets/grades-CM1ebojp.js +1 -0
- torre/ui/assets/i18n-C6NUi9Vs.js +2 -0
- torre/ui/assets/i18n-en-BnZCUyK7.js +1 -0
- torre/ui/assets/i18n-es-SAH-03ta.js +1 -0
- torre/ui/assets/index-Cl_QBZkR.js +2 -0
- torre/ui/assets/index-Zp2E1Y4q.css +1 -0
- torre/ui/assets/lastVisited-ChiJXsfH.js +1 -0
- torre/ui/assets/rolldown-runtime-hePW80VL.js +1 -0
- torre/ui/assets/routes-C8cKcny6.js +1 -0
- torre/ui/assets/stars-De-qIuRF.js +1 -0
- torre/ui/assets/storage-CWyoBvAQ.js +1 -0
- torre/ui/assets/story-DGV08-e8.js +1 -0
- torre/ui/assets/useChess-DdakFbd7.js +1 -0
- torre/ui/assets/useMasters-CW1vT-o3.js +1 -0
- torre/ui/assets/useNarration-IQPX4VHr.js +1 -0
- torre/ui/assets/usePlanning-BfNQrrQD.js +1 -0
- torre/ui/assets/useProfile-COaif7mf.js +1 -0
- torre/ui/assets/useSounds-DtGm8WF1.js +1 -0
- torre/ui/assets/vendor-DVQGMHsh.js +12 -0
- torre/ui/assets/whyItFails-CQZuFn5V.js +1 -0
- torre/ui/index.html +23 -0
- torre/ui/locales/en/arena.json +180 -0
- torre/ui/locales/en/awards.json +21 -0
- torre/ui/locales/en/board.json +281 -0
- torre/ui/locales/en/coach.json +138 -0
- torre/ui/locales/en/coachlines.json +217 -0
- torre/ui/locales/en/commentary.json +862 -0
- torre/ui/locales/en/common.json +32 -0
- torre/ui/locales/en/content.json +6210 -0
- torre/ui/locales/en/endgames.json +68 -0
- torre/ui/locales/en/engineProfile.json +17 -0
- torre/ui/locales/en/engines.json +71 -0
- torre/ui/locales/en/games.json +130 -0
- torre/ui/locales/en/home.json +80 -0
- torre/ui/locales/en/import.json +104 -0
- torre/ui/locales/en/insights.json +110 -0
- torre/ui/locales/en/lessons.json +207 -0
- torre/ui/locales/en/nav.json +106 -0
- torre/ui/locales/en/openings.json +66 -0
- torre/ui/locales/en/play.json +405 -0
- torre/ui/locales/en/profile.json +129 -0
- torre/ui/locales/en/puzzles.json +179 -0
- torre/ui/locales/en/rooky.json +83 -0
- torre/ui/locales/en/settings.json +344 -0
- torre/ui/locales/en/setup.json +10 -0
- torre/ui/locales/en/stats.json +72 -0
- torre/ui/locales/en/story.json +151 -0
- torre/ui/locales/en/train.json +82 -0
- torre/ui/locales/en/trainer.json +81 -0
- torre/ui/locales/en/voice.json +91 -0
- torre/ui/locales/es/arena.json +180 -0
- torre/ui/locales/es/awards.json +21 -0
- torre/ui/locales/es/board.json +281 -0
- torre/ui/locales/es/coach.json +138 -0
- torre/ui/locales/es/coachlines.json +217 -0
- torre/ui/locales/es/commentary.json +890 -0
- torre/ui/locales/es/common.json +32 -0
- torre/ui/locales/es/content.json +6210 -0
- torre/ui/locales/es/endgames.json +68 -0
- torre/ui/locales/es/engineProfile.json +17 -0
- torre/ui/locales/es/engines.json +71 -0
- torre/ui/locales/es/games.json +130 -0
- torre/ui/locales/es/home.json +80 -0
- torre/ui/locales/es/import.json +104 -0
- torre/ui/locales/es/insights.json +110 -0
- torre/ui/locales/es/lessons.json +207 -0
- torre/ui/locales/es/nav.json +106 -0
- torre/ui/locales/es/openings.json +66 -0
- torre/ui/locales/es/play.json +405 -0
- torre/ui/locales/es/profile.json +129 -0
- torre/ui/locales/es/puzzles.json +179 -0
- torre/ui/locales/es/rooky.json +83 -0
- torre/ui/locales/es/settings.json +344 -0
- torre/ui/locales/es/setup.json +10 -0
- torre/ui/locales/es/stats.json +72 -0
- torre/ui/locales/es/story.json +151 -0
- torre/ui/locales/es/train.json +82 -0
- torre/ui/locales/es/trainer.json +81 -0
- torre/ui/locales/es/voice.json +91 -0
- torre/voice/__init__.py +1 -0
- torre/voice/narrate.py +305 -0
- torre-0.3.0.dist-info/METADATA +414 -0
- torre-0.3.0.dist-info/RECORD +1834 -0
- torre-0.3.0.dist-info/WHEEL +5 -0
- torre-0.3.0.dist-info/entry_points.txt +2 -0
- torre-0.3.0.dist-info/licenses/LICENSE +674 -0
- torre-0.3.0.dist-info/top_level.txt +1 -0
torre/__init__.py
ADDED
torre/__main__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Engines, analysis and scoring: Stockfish runs, move grades, motifs, stats, reports and exports."""
|
|
@@ -0,0 +1,337 @@
|
|
|
1
|
+
"""Analyze games with Stockfish: Lichess-style judgments and accuracy.
|
|
2
|
+
|
|
3
|
+
Reads: data/games.jsonl
|
|
4
|
+
Writes: data/analysis.jsonl (resume-safe; one record per game)
|
|
5
|
+
|
|
6
|
+
Moves are judged the way Lichess does it, by the drop in winning chances
|
|
7
|
+
(inaccuracy / mistake / blunder), and game accuracy uses Lichess's formula;
|
|
8
|
+
see scoring.py. Moves matching the engine's choice are tagged "best", and
|
|
9
|
+
sound sacrifices "brilliant". Centipawn loss (capped at +/-1500, mate beyond
|
|
10
|
+
the cap) is still recorded for the ACPL statistics.
|
|
11
|
+
|
|
12
|
+
Records from an older SCORING_VERSION are re-analyzed first. analyze_archive is
|
|
13
|
+
the whole pipeline (analysis, then puzzles mined from the new analyses) behind
|
|
14
|
+
`torre analyze` and the studio's background analysis.
|
|
15
|
+
|
|
16
|
+
Usage: .venv/Scripts/python analyze.py [--depth 12] [--limit N] (the same as torre analyze)
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
import io
|
|
20
|
+
import json
|
|
21
|
+
import logging
|
|
22
|
+
import sys
|
|
23
|
+
import threading
|
|
24
|
+
from pathlib import Path
|
|
25
|
+
|
|
26
|
+
import chess
|
|
27
|
+
import chess.engine
|
|
28
|
+
import chess.pgn
|
|
29
|
+
|
|
30
|
+
from torre.analysis import scoring
|
|
31
|
+
from torre.analysis import engines
|
|
32
|
+
from torre import jsonio
|
|
33
|
+
GAMES_FILE = Path("data/games.jsonl")
|
|
34
|
+
ANALYSIS_FILE = Path("data/analysis.jsonl")
|
|
35
|
+
|
|
36
|
+
# v3: shared eight-grade classification, opening provenance and runner-up evaluations.
|
|
37
|
+
SCORING_VERSION = 3
|
|
38
|
+
EVAL_CAP = 1500
|
|
39
|
+
DEFAULT_DEPTH = 12
|
|
40
|
+
MATED_CP = 100_000 # stored cp for a checkmated side (White POV sign)
|
|
41
|
+
# Appends to analysis files and their compaction take turns (within this process).
|
|
42
|
+
ANALYSIS_LOCK = threading.Lock()
|
|
43
|
+
log = logging.getLogger(__name__)
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def phase_of(fullmove: int) -> str:
|
|
47
|
+
if fullmove <= 10:
|
|
48
|
+
return "opening"
|
|
49
|
+
if fullmove <= 30:
|
|
50
|
+
return "middlegame"
|
|
51
|
+
return "endgame"
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def evaluate(engine, board: chess.Board, depth: int, alternatives=None):
|
|
55
|
+
"""White-POV [cp, mate] for `board`, plus the engine's best move (uci)."""
|
|
56
|
+
if board.is_checkmate():
|
|
57
|
+
if alternatives is not None:
|
|
58
|
+
alternatives.append(None)
|
|
59
|
+
return [-MATED_CP if board.turn == chess.WHITE else MATED_CP, None], None
|
|
60
|
+
if board.is_stalemate() or board.is_insufficient_material():
|
|
61
|
+
if alternatives is not None:
|
|
62
|
+
alternatives.append(None)
|
|
63
|
+
return [0, None], None
|
|
64
|
+
infos = engine.analyse(board, chess.engine.Limit(depth=depth), multipv=2)
|
|
65
|
+
info = infos[0]
|
|
66
|
+
if alternatives is not None:
|
|
67
|
+
runner = infos[1]['score'].white() if len(infos) > 1 else None
|
|
68
|
+
alternatives.append([runner.score(), runner.mate()] if runner is not None else None)
|
|
69
|
+
score = info["score"].white()
|
|
70
|
+
pv = info.get("pv") or []
|
|
71
|
+
best = pv[0].uci() if pv else None
|
|
72
|
+
if score.is_mate():
|
|
73
|
+
return [None, score.mate()], best
|
|
74
|
+
return [score.score(), None], best
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def legacy_cp(ev, pov: chess.Color) -> int:
|
|
78
|
+
"""Capped centipawns from `pov`, mate beyond the cap (the ACPL scale)."""
|
|
79
|
+
cp, mate = ev
|
|
80
|
+
sign = 1 if pov == chess.WHITE else -1
|
|
81
|
+
if mate is not None or abs(cp) >= MATED_CP:
|
|
82
|
+
winning = (mate if mate is not None else cp) * sign > 0
|
|
83
|
+
return (EVAL_CAP + 1000) if winning else -(EVAL_CAP + 1000)
|
|
84
|
+
return max(-EVAL_CAP, min(EVAL_CAP, cp * sign))
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def judge_ply(board: chess.Board, move: chess.Move, before, after):
|
|
88
|
+
"""(win_before, win_after, judgment, brilliant) for `move` played in
|
|
89
|
+
`board`, from the mover's point of view. `before` / `after` are the stored
|
|
90
|
+
White-POV evals around the move."""
|
|
91
|
+
white = board.turn == chess.WHITE
|
|
92
|
+
win_before, win_after = scoring.win_pct(*before), scoring.win_pct(*after)
|
|
93
|
+
if not white:
|
|
94
|
+
win_before, win_after = 100 - win_before, 100 - win_after
|
|
95
|
+
if after[0] is not None and abs(after[0]) >= MATED_CP: # the move gives checkmate
|
|
96
|
+
return win_before, win_after, None, False
|
|
97
|
+
cls = scoring.judge(before, after, white)
|
|
98
|
+
brilliant = cls is None and scoring.is_brilliant(board, move, win_before, win_after)
|
|
99
|
+
return win_before, win_after, cls, brilliant
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def analyze_game(engine, rec: dict, depth: int) -> dict:
|
|
103
|
+
game = chess.pgn.read_game(io.StringIO(rec["pgn"]))
|
|
104
|
+
user_color = chess.WHITE if rec["user_color"] == "white" else chess.BLACK
|
|
105
|
+
standard_start = game.board().fen() == chess.STARTING_FEN
|
|
106
|
+
|
|
107
|
+
# Evaluate the start position and the position after every ply.
|
|
108
|
+
moves = list(game.mainline_moves())
|
|
109
|
+
board = game.board()
|
|
110
|
+
evals, best_moves, alternatives = [], [], []
|
|
111
|
+
for mv in moves:
|
|
112
|
+
ev, best = evaluate(engine, board, depth, alternatives)
|
|
113
|
+
evals.append(ev)
|
|
114
|
+
best_moves.append(best)
|
|
115
|
+
board.push(mv)
|
|
116
|
+
final_ev, _ = evaluate(engine, board, depth)
|
|
117
|
+
evals.append(final_ev)
|
|
118
|
+
|
|
119
|
+
win = [scoring.win_pct(*ev) for ev in evals]
|
|
120
|
+
acc_timeline = [scoring.win_pct(scoring.STANDARD_START_CP)] + win[1:] if standard_start else win
|
|
121
|
+
accuracy = scoring.game_accuracy(acc_timeline, white_starts=game.board().turn == chess.WHITE)
|
|
122
|
+
|
|
123
|
+
user_moves, all_grades = [], []
|
|
124
|
+
board = game.board()
|
|
125
|
+
for i, mv in enumerate(moves):
|
|
126
|
+
mover = board.turn
|
|
127
|
+
graded = scoring.classify(board, mv,
|
|
128
|
+
{'cp': evals[i][0], 'mate': evals[i][1], 'best': best_moves[i], 'alternative': alternatives[i]},
|
|
129
|
+
{'cp': evals[i + 1][0], 'mate': evals[i + 1][1]})
|
|
130
|
+
all_grades.append(graded)
|
|
131
|
+
if mover == user_color:
|
|
132
|
+
win_before, win_after, cls, brilliant = judge_ply(board, mv, evals[i], evals[i + 1])
|
|
133
|
+
before = legacy_cp(evals[i], mover)
|
|
134
|
+
cpl = max(0, before - legacy_cp(evals[i + 1], mover))
|
|
135
|
+
user_moves.append({
|
|
136
|
+
"ply": i + 1,
|
|
137
|
+
"move_number": board.fullmove_number,
|
|
138
|
+
"san": board.san(mv),
|
|
139
|
+
"fen": board.fen(),
|
|
140
|
+
"phase": phase_of(board.fullmove_number),
|
|
141
|
+
"eval_before": before,
|
|
142
|
+
"cpl": cpl,
|
|
143
|
+
"win_before": round(win_before, 1),
|
|
144
|
+
"win_after": round(win_after, 1),
|
|
145
|
+
**graded,
|
|
146
|
+
})
|
|
147
|
+
board.push(mv)
|
|
148
|
+
|
|
149
|
+
counts = {"inaccuracy": 0, "mistake": 0, "blunder": 0}
|
|
150
|
+
for m in user_moves:
|
|
151
|
+
if m["class"]:
|
|
152
|
+
counts[m["class"]] += 1
|
|
153
|
+
|
|
154
|
+
worst = sorted((m for m in user_moves if m["class"]), key=lambda m: m["win_after"] - m["win_before"])[:5]
|
|
155
|
+
opp = "black" if user_color == chess.WHITE else "white"
|
|
156
|
+
rounded = lambda x: None if x is None else round(x, 1)
|
|
157
|
+
|
|
158
|
+
return {
|
|
159
|
+
"id": rec["id"],
|
|
160
|
+
"scoring_version": SCORING_VERSION,
|
|
161
|
+
"move_grades": all_grades,
|
|
162
|
+
"alternatives": alternatives,
|
|
163
|
+
"grades": {name: sum(scoring.grade(m) == name for m in user_moves) for name in scoring.GRADES},
|
|
164
|
+
"date": rec.get("date"),
|
|
165
|
+
"type": rec.get("type"),
|
|
166
|
+
"opponent": rec.get("opponent"),
|
|
167
|
+
"user_color": rec["user_color"],
|
|
168
|
+
"user_result": rec.get("user_result"),
|
|
169
|
+
"user_accuracy": rec.get("user_accuracy"),
|
|
170
|
+
"accuracy": rounded(accuracy[rec["user_color"]]),
|
|
171
|
+
"opp_accuracy": rounded(accuracy[opp]),
|
|
172
|
+
"opp_rating": rec.get("opp_rating"),
|
|
173
|
+
"user_rating": rec.get("user_rating"),
|
|
174
|
+
"user_move_count": len(user_moves),
|
|
175
|
+
"avg_cpl": round(sum(m["cpl"] for m in user_moves) / max(1, len(user_moves)), 1),
|
|
176
|
+
**counts,
|
|
177
|
+
"best": sum(m["best"] for m in user_moves),
|
|
178
|
+
"brilliant": sum(m["brilliant"] for m in user_moves),
|
|
179
|
+
"phase_counts": {
|
|
180
|
+
ph: {
|
|
181
|
+
"moves": sum(1 for m in user_moves if m["phase"] == ph),
|
|
182
|
+
"cpl": sum(m["cpl"] for m in user_moves if m["phase"] == ph),
|
|
183
|
+
"blunders": sum(1 for m in user_moves if m["phase"] == ph and m["class"] == "blunder"),
|
|
184
|
+
"mistakes": sum(1 for m in user_moves if m["phase"] == ph and m["class"] == "mistake"),
|
|
185
|
+
}
|
|
186
|
+
for ph in ("opening", "middlegame", "endgame")
|
|
187
|
+
},
|
|
188
|
+
"worst_moves": worst,
|
|
189
|
+
"key_moments": scoring.key_moments(user_moves),
|
|
190
|
+
"moves": [{k: m[k] for k in ("ply", "san", "class", "best", "brilliant", "great", "book", "accuracy")} for m in user_moves],
|
|
191
|
+
"evals": evals,
|
|
192
|
+
"best_moves": best_moves,
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
def review_moves(record):
|
|
197
|
+
"""Player moves with the same flags as the full grading timeline, when available."""
|
|
198
|
+
timeline = record.get('move_grades') or []
|
|
199
|
+
return [{**m, **(timeline[m['ply'] - 1] if 0 < m['ply'] <= len(timeline) else {})}
|
|
200
|
+
for m in record.get('moves') or []]
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
def _latest(rows) -> dict:
|
|
204
|
+
"""One record per game id, preferring the current scoring version, then the latest written."""
|
|
205
|
+
records = {}
|
|
206
|
+
for rec in rows:
|
|
207
|
+
if not isinstance(rec, dict) or "id" not in rec:
|
|
208
|
+
continue
|
|
209
|
+
gid = rec["id"]
|
|
210
|
+
old = records.get(gid)
|
|
211
|
+
if old is None or rec.get("scoring_version") == SCORING_VERSION or old.get("scoring_version") != SCORING_VERSION:
|
|
212
|
+
records[gid] = rec
|
|
213
|
+
return records
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
def latest(analysis_file: Path = ANALYSIS_FILE) -> dict:
|
|
217
|
+
"""The analysis records by game id (see _latest), without changing the file. Shared: read-only."""
|
|
218
|
+
return jsonio.cached(Path(analysis_file), _latest_in)
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
def _latest_in(path):
|
|
222
|
+
return _latest(jsonio.read_jsonl(path))
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
def compact(analysis_file: Path = ANALYSIS_FILE) -> dict:
|
|
226
|
+
"""Rewrite the analysis file with one record per game (see _latest), in one atomic step and
|
|
227
|
+
never while this process appends to it. Returns the records by id."""
|
|
228
|
+
analysis_file = Path(analysis_file)
|
|
229
|
+
with ANALYSIS_LOCK:
|
|
230
|
+
if not analysis_file.exists():
|
|
231
|
+
return {}
|
|
232
|
+
lines, rows = 0, []
|
|
233
|
+
with analysis_file.open(encoding="utf-8") as f:
|
|
234
|
+
for line in f:
|
|
235
|
+
lines += 1
|
|
236
|
+
try:
|
|
237
|
+
rows.append(json.loads(line))
|
|
238
|
+
except json.JSONDecodeError:
|
|
239
|
+
continue
|
|
240
|
+
records = _latest(rows)
|
|
241
|
+
if lines != len(records):
|
|
242
|
+
try:
|
|
243
|
+
jsonio.write_atomic(analysis_file, "".join(json.dumps(r) + "\n" for r in records.values()))
|
|
244
|
+
except OSError: # the file stays as it is: readers already skip the duplicates
|
|
245
|
+
log.warning("Could not compact %s", analysis_file, exc_info=True)
|
|
246
|
+
return records
|
|
247
|
+
|
|
248
|
+
|
|
249
|
+
def pending(games_file: Path = GAMES_FILE, analysis_file: Path = ANALYSIS_FILE):
|
|
250
|
+
"""Games still to analyze (outdated records first), and how many are current. Read-only:
|
|
251
|
+
the analysis file is compacted by the analysis run, never here."""
|
|
252
|
+
records = latest(analysis_file)
|
|
253
|
+
done = {gid for gid, rec in records.items() if rec.get("scoring_version") == SCORING_VERSION}
|
|
254
|
+
outdated = set(records) - done
|
|
255
|
+
games = [rec for rec in jsonio.read_jsonl(Path(games_file))
|
|
256
|
+
if isinstance(rec, dict) and "id" in rec and rec["id"] not in done]
|
|
257
|
+
# Outdated records first, then the newest games: recent mistakes are the ones worth drilling.
|
|
258
|
+
games.sort(key=lambda r: r.get("date") or "", reverse=True)
|
|
259
|
+
games.sort(key=lambda r: r["id"] not in outdated)
|
|
260
|
+
return games, len(done), len(outdated)
|
|
261
|
+
|
|
262
|
+
|
|
263
|
+
def run(games, engine_path, analysis_file: Path = ANALYSIS_FILE, depth=DEFAULT_DEPTH, threads=8,
|
|
264
|
+
progress=None, should_stop=lambda: False):
|
|
265
|
+
"""Analyze `games`, appending each record as it finishes. `progress` is
|
|
266
|
+
called as progress(index, game, result_or_None, error_or_None)."""
|
|
267
|
+
engine = chess.engine.SimpleEngine.popen_uci(str(engine_path))
|
|
268
|
+
engine.configure({"Threads": threads, "Hash": 512})
|
|
269
|
+
try:
|
|
270
|
+
for i, rec in enumerate(games, 1):
|
|
271
|
+
if should_stop():
|
|
272
|
+
break
|
|
273
|
+
try:
|
|
274
|
+
result = analyze_game(engine, rec, depth)
|
|
275
|
+
except Exception as e: # one unreadable game must not stop the run
|
|
276
|
+
if progress:
|
|
277
|
+
progress(i, rec, None, e)
|
|
278
|
+
continue
|
|
279
|
+
# Opened per record, so compact() can replace the file between two of them.
|
|
280
|
+
with ANALYSIS_LOCK, Path(analysis_file).open("a", encoding="utf-8") as out:
|
|
281
|
+
out.write(json.dumps(result) + "\n")
|
|
282
|
+
if progress:
|
|
283
|
+
progress(i, rec, result, None)
|
|
284
|
+
finally:
|
|
285
|
+
engine.quit()
|
|
286
|
+
compact(analysis_file)
|
|
287
|
+
|
|
288
|
+
|
|
289
|
+
def analyze_archive(games, engine_path, games_file, analysis_file, pack_file, *, depth=DEFAULT_DEPTH, threads=8,
|
|
290
|
+
batch=None, mine=True, progress=None, mine_progress=None, on_mine=None, should_stop=lambda: False):
|
|
291
|
+
"""Analyze `games` (pending()'s list), then mine the new analyses into the "From your games"
|
|
292
|
+
puzzles in `pack_file` (my_puzzles.py). Returns (pack, games mined), the pack being None
|
|
293
|
+
when nothing was mined.
|
|
294
|
+
|
|
295
|
+
With `batch`, mining follows every `batch` games, so a long run stopped halfway already feeds
|
|
296
|
+
the puzzles and drills. Each engine takes up to `threads` from engines.POOL.budget.
|
|
297
|
+
progress(index, game, result, error) follows each game and mine_progress (my_puzzles.run's)
|
|
298
|
+
each mined one. on_mine('start', None) comes before each mining pass, then
|
|
299
|
+
on_mine('done', (pack, mined)) or on_mine('failed', exc): a failed pass is then reported
|
|
300
|
+
and the run goes on; without on_mine it is raised.
|
|
301
|
+
"""
|
|
302
|
+
from torre.learning import my_puzzles # it imports this module
|
|
303
|
+
batch = batch or len(games) or 1
|
|
304
|
+
pack, total = None, 0
|
|
305
|
+
for start in range(0, max(len(games), 1), batch):
|
|
306
|
+
chunk = games[start:start + batch]
|
|
307
|
+
if chunk:
|
|
308
|
+
with engines.POOL.budget.reserve(threads) as granted:
|
|
309
|
+
run(chunk, engine_path, analysis_file, depth=depth, threads=granted, should_stop=should_stop,
|
|
310
|
+
progress=progress and (lambda i, *rest, at=start: progress(at + i, *rest)))
|
|
311
|
+
if mine:
|
|
312
|
+
if on_mine:
|
|
313
|
+
on_mine('start', None)
|
|
314
|
+
try:
|
|
315
|
+
with engines.POOL.budget.reserve(threads) as granted:
|
|
316
|
+
pack, mined = my_puzzles.run(games_file, analysis_file, pack_file=pack_file, engine_path=engine_path,
|
|
317
|
+
threads=granted, progress=mine_progress)
|
|
318
|
+
except Exception as exc: # a failed mine never hides the analysis itself
|
|
319
|
+
if not on_mine:
|
|
320
|
+
raise
|
|
321
|
+
on_mine('failed', exc)
|
|
322
|
+
else:
|
|
323
|
+
total += mined
|
|
324
|
+
if on_mine:
|
|
325
|
+
on_mine('done', (pack, mined))
|
|
326
|
+
if should_stop():
|
|
327
|
+
break
|
|
328
|
+
return pack, total
|
|
329
|
+
|
|
330
|
+
|
|
331
|
+
def main(argv=None):
|
|
332
|
+
from torre import cli as torre
|
|
333
|
+
return torre.main(['analyze', *(sys.argv[1:] if argv is None else argv)])
|
|
334
|
+
|
|
335
|
+
|
|
336
|
+
if __name__ == "__main__":
|
|
337
|
+
raise SystemExit(main())
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
"""Boards as the app sees them: a position from a start and a move list, what the board page
|
|
2
|
+
draws for it (state), and games read from PGN."""
|
|
3
|
+
import io
|
|
4
|
+
|
|
5
|
+
import chess
|
|
6
|
+
import chess.pgn
|
|
7
|
+
|
|
8
|
+
from torre.analysis import opening_book
|
|
9
|
+
from torre.server.routes import BadRequest
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def moves_of(payload):
|
|
13
|
+
"""The request's "moves": a list of UCI strings (none when absent)."""
|
|
14
|
+
moves = payload.get('moves', [])
|
|
15
|
+
if not isinstance(moves, list) or not all(isinstance(uci, str) for uci in moves):
|
|
16
|
+
raise BadRequest('"moves" must be a list of moves.')
|
|
17
|
+
return moves
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def position(payload):
|
|
21
|
+
fen = payload.get('initialFen') or chess.STARTING_FEN
|
|
22
|
+
if not isinstance(fen, str):
|
|
23
|
+
raise BadRequest('This position is not a valid chess position.')
|
|
24
|
+
board = chess.Board(fen)
|
|
25
|
+
if not board.is_valid():
|
|
26
|
+
raise ValueError('This position is not a valid chess position.')
|
|
27
|
+
for uci in moves_of(payload):
|
|
28
|
+
move = chess.Move.from_uci(uci)
|
|
29
|
+
if move not in board.legal_moves:
|
|
30
|
+
raise ValueError('The move history contains an illegal move.')
|
|
31
|
+
board.push(move)
|
|
32
|
+
return board
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def line_sans(payload):
|
|
36
|
+
"""SAN for a line of UCI moves, cut at the first illegal one (with an error)."""
|
|
37
|
+
board = position({'initialFen': payload.get('initialFen')})
|
|
38
|
+
moves = []
|
|
39
|
+
for uci in moves_of(payload):
|
|
40
|
+
try:
|
|
41
|
+
move = chess.Move.from_uci(uci)
|
|
42
|
+
except (ValueError, TypeError):
|
|
43
|
+
move = None
|
|
44
|
+
if move is None or move not in board.legal_moves:
|
|
45
|
+
return {'moves': moves, 'error': 'This line contains an illegal move. It was cut at the last legal one.'}
|
|
46
|
+
moves.append({'uci': move.uci(), 'san': board.san(move)})
|
|
47
|
+
board.push(move)
|
|
48
|
+
return {'moves': moves}
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def illegal_reason(board, from_square, to_square):
|
|
52
|
+
"""Why the side to move cannot play from `from_square` to `to_square`, as a rule a learner can
|
|
53
|
+
read: (reason, details) or None when the move is legal or there is nothing to explain (an empty
|
|
54
|
+
square, the other side's piece, or the mover's own piece in the way, which selects it instead).
|
|
55
|
+
|
|
56
|
+
Reasons: pinned (the piece shields its king from `attacker`), inCheck (the king is in check from
|
|
57
|
+
`attacker` and this does not answer it), kingAttacked (the king would step onto a square
|
|
58
|
+
`attacker` covers), exposesKing (it would uncover the king to `attacker`), castleInCheck,
|
|
59
|
+
castleThroughCheck, castleRights, castleBlocked, pathBlocked (`blocker` stands in the way),
|
|
60
|
+
pawnForward (pawns capture diagonally, never straight ahead), pawnDiagonal (a pawn moves
|
|
61
|
+
diagonally only to capture), pawnDouble (two squares only from the starting rank), movement
|
|
62
|
+
(that piece never moves that way)."""
|
|
63
|
+
piece = board.piece_at(from_square)
|
|
64
|
+
if piece is None or piece.color != board.turn:
|
|
65
|
+
return None
|
|
66
|
+
target = board.piece_at(to_square)
|
|
67
|
+
if target is not None and target.color == board.turn:
|
|
68
|
+
return None
|
|
69
|
+
king = board.king(board.turn)
|
|
70
|
+
name = chess.piece_name(piece.piece_type)
|
|
71
|
+
moves = [m for m in (chess.Move(from_square, to_square, promotion=p) for p in (None, chess.QUEEN)) if board.is_pseudo_legal(m)]
|
|
72
|
+
if any(board.is_legal(m) for m in moves) or (board.piece_type_at(from_square) == chess.KING and any(
|
|
73
|
+
m.from_square == from_square and m.to_square == to_square for m in board.generate_castling_moves())):
|
|
74
|
+
return None
|
|
75
|
+
castle = piece.piece_type == chess.KING and chess.square_rank(from_square) == chess.square_rank(to_square) \
|
|
76
|
+
and abs(chess.square_file(from_square) - chess.square_file(to_square)) == 2
|
|
77
|
+
if castle:
|
|
78
|
+
rook_file = 7 if chess.square_file(to_square) > chess.square_file(from_square) else 0
|
|
79
|
+
rook = chess.square(rook_file, chess.square_rank(from_square))
|
|
80
|
+
if not board.has_castling_rights(board.turn) or not (board.castling_rights & chess.BB_SQUARES[rook]):
|
|
81
|
+
return 'castleRights', {}
|
|
82
|
+
if board.is_check():
|
|
83
|
+
return 'castleInCheck', {}
|
|
84
|
+
step = 1 if rook_file == 7 else -1
|
|
85
|
+
between = range(chess.square_file(from_square) + step, rook_file, step)
|
|
86
|
+
if any(board.piece_at(chess.square(f, chess.square_rank(from_square))) for f in between):
|
|
87
|
+
return 'castleBlocked', {}
|
|
88
|
+
return 'castleThroughCheck', {}
|
|
89
|
+
if moves:
|
|
90
|
+
move = moves[0]
|
|
91
|
+
after = board.copy(stack=False)
|
|
92
|
+
after.push(move)
|
|
93
|
+
king_after = after.king(board.turn)
|
|
94
|
+
attackers = sorted(after.attackers(not board.turn, king_after))
|
|
95
|
+
attacker = {'attacker': chess.square_name(attackers[0])} if attackers else {}
|
|
96
|
+
if piece.piece_type == chess.KING:
|
|
97
|
+
return 'kingAttacked', attacker
|
|
98
|
+
if board.is_check():
|
|
99
|
+
return 'inCheck', {'attacker': chess.square_name(sorted(board.checkers())[0])}
|
|
100
|
+
if board.is_pinned(board.turn, from_square):
|
|
101
|
+
return 'pinned', {'piece': name, **attacker}
|
|
102
|
+
return 'exposesKing', attacker
|
|
103
|
+
if piece.piece_type == chess.PAWN:
|
|
104
|
+
forward = 8 if board.turn == chess.WHITE else -8
|
|
105
|
+
start_rank = 1 if board.turn == chess.WHITE else 6
|
|
106
|
+
files = chess.square_file(to_square) - chess.square_file(from_square)
|
|
107
|
+
if files == 0 and to_square - from_square in (forward, 2 * forward):
|
|
108
|
+
if to_square - from_square == 2 * forward and chess.square_rank(from_square) != start_rank:
|
|
109
|
+
return 'pawnDouble', {}
|
|
110
|
+
return 'pawnForward', {}
|
|
111
|
+
if abs(files) == 1 and to_square - from_square in (forward - 1, forward + 1):
|
|
112
|
+
return 'pawnDiagonal', {}
|
|
113
|
+
return 'movement', {'piece': name}
|
|
114
|
+
# A slider aimed along its own line, with something between.
|
|
115
|
+
if piece.piece_type in (chess.BISHOP, chess.ROOK, chess.QUEEN) and to_square in chess.SquareSet(chess.BB_RAYS[from_square][to_square]):
|
|
116
|
+
diagonal = chess.square_file(from_square) != chess.square_file(to_square) and chess.square_rank(from_square) != chess.square_rank(to_square)
|
|
117
|
+
fits = piece.piece_type == chess.QUEEN or (piece.piece_type == chess.BISHOP) == diagonal
|
|
118
|
+
between = chess.SquareSet(chess.between(from_square, to_square)) & board.occupied
|
|
119
|
+
if fits and between:
|
|
120
|
+
return 'pathBlocked', {'piece': name, 'blocker': chess.square_name(min(between, key=lambda s: chess.square_distance(s, from_square)))}
|
|
121
|
+
return 'movement', {'piece': name}
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
def board_guidance(board):
|
|
125
|
+
"""Attacks describe control; planning paths also respect blockers and king safety."""
|
|
126
|
+
plans = {}
|
|
127
|
+
for color in (chess.WHITE, chess.BLACK):
|
|
128
|
+
hypothetical = board.copy(stack=False)
|
|
129
|
+
hypothetical.turn = color
|
|
130
|
+
if color != board.turn:
|
|
131
|
+
hypothetical.ep_square = None
|
|
132
|
+
for move in hypothetical.legal_moves:
|
|
133
|
+
target = hypothetical.piece_at(move.to_square)
|
|
134
|
+
if target and target.piece_type == chess.KING:
|
|
135
|
+
continue
|
|
136
|
+
origin = chess.square_name(move.from_square)
|
|
137
|
+
destination = chess.square_name(move.to_square)
|
|
138
|
+
if destination not in plans.setdefault(origin, []):
|
|
139
|
+
plans[origin].append(destination)
|
|
140
|
+
control = {chess.square_name(square): {
|
|
141
|
+
'white': [chess.square_name(s) for s in board.attackers(chess.WHITE, square)],
|
|
142
|
+
'black': [chess.square_name(s) for s in board.attackers(chess.BLACK, square)],
|
|
143
|
+
} for square in chess.SQUARES}
|
|
144
|
+
return {'plans': plans, 'control': control}
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
def state(board):
|
|
148
|
+
outcome = board.outcome(claim_draw=True)
|
|
149
|
+
return {
|
|
150
|
+
**board_guidance(board),
|
|
151
|
+
'fen': board.fen(), 'turn': 'white' if board.turn else 'black',
|
|
152
|
+
'pieces': {chess.square_name(s): p.symbol() for s, p in board.piece_map().items()},
|
|
153
|
+
'legal': [m.uci() for m in board.legal_moves],
|
|
154
|
+
'check': chess.square_name(board.king(board.turn)) if board.is_check() else None,
|
|
155
|
+
'result': outcome.result() if outcome else None,
|
|
156
|
+
'termination': outcome.termination.name.replace('_', ' ').lower() if outcome else None,
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
def parse_pgn(pgn):
|
|
161
|
+
if not isinstance(pgn, str) or not pgn.strip():
|
|
162
|
+
raise ValueError('Paste a PGN game to import.')
|
|
163
|
+
game = chess.pgn.read_game(io.StringIO(pgn))
|
|
164
|
+
if game is None or game.errors:
|
|
165
|
+
raise ValueError('Could not read this PGN. Check the headers and move notation.')
|
|
166
|
+
board = game.board()
|
|
167
|
+
initial = board.fen()
|
|
168
|
+
if not board.is_valid():
|
|
169
|
+
raise ValueError('The PGN starts from an invalid position.')
|
|
170
|
+
moves = []
|
|
171
|
+
for node in game.mainline():
|
|
172
|
+
move, clock = node.move, node.clock()
|
|
173
|
+
moves.append({'uci': move.uci(), 'san': board.san(move), **({'clock': round(clock, 1)} if clock is not None else {})})
|
|
174
|
+
board.push(move)
|
|
175
|
+
if not moves and not pgn.lstrip().startswith('['):
|
|
176
|
+
raise ValueError('No chess moves were found. Paste PGN headers and move notation.')
|
|
177
|
+
return {'initialFen': initial, 'moves': moves, 'headers': dict(game.headers), 'opening': opening_book.recognize(game)}
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
def with_clocks(detail, record):
|
|
181
|
+
"""Clocks for a game whose PGN has none, from the chess.com timestamps saved with it (tenths of a
|
|
182
|
+
second left after each move, from the extension export and the callback API)."""
|
|
183
|
+
stamps, moves = record.get('move_timestamps'), detail['moves']
|
|
184
|
+
if isinstance(stamps, str): # the callback API sends "5991,5980,...", the extension a list
|
|
185
|
+
try:
|
|
186
|
+
stamps = [float(stamp) for stamp in stamps.split(',')]
|
|
187
|
+
except ValueError:
|
|
188
|
+
return detail
|
|
189
|
+
# Live games carry one more value than moves, after the last: each move's clock is still in order.
|
|
190
|
+
if not isinstance(stamps, list) or len(stamps) not in (len(moves), len(moves) + 1) or any('clock' in move for move in moves):
|
|
191
|
+
return detail
|
|
192
|
+
stamps = stamps[:len(moves)]
|
|
193
|
+
if not all(isinstance(stamp, (int, float)) and stamp >= 0 for stamp in stamps):
|
|
194
|
+
return detail
|
|
195
|
+
return {**detail, 'moves': [{**move, 'clock': round(stamp / 10, 1)} for move, stamp in zip(moves, stamps)]}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
"""How a game against a bot was really played, phase by phase: the after-game card that shows
|
|
2
|
+
whether the bot kept to the strength it was set to.
|
|
3
|
+
|
|
4
|
+
A bot plays every move at the rating it was given (one rating, or one per phase; see
|
|
5
|
+
api_board._phase_elo), and weak bots now and then play a deliberate weaker move (a "slip",
|
|
6
|
+
engines.strength_options). Neither changes during a game on its own. This module puts numbers
|
|
7
|
+
on that, from the engine's review of the finished game:
|
|
8
|
+
|
|
9
|
+
phases accuracy and errors for you and the bot in the opening, middlegame and endgame
|
|
10
|
+
(game_stats.position_phase; Lichess accuracy, game_stats.aggregate_accuracy)
|
|
11
|
+
slips the bot's deliberate weaker moves and how each turned out: one can land on a
|
|
12
|
+
good move by chance, which is why a weak bot sometimes looks strong
|
|
13
|
+
ratings the rating the bot was set to in each phase it played
|
|
14
|
+
retry your costliest mistake, with the position before it, to play again from there
|
|
15
|
+
|
|
16
|
+
All evals are White-POV [cp, mate] pairs, one per position (the start and after every move).
|
|
17
|
+
"""
|
|
18
|
+
import chess
|
|
19
|
+
|
|
20
|
+
from torre.analysis import game_stats
|
|
21
|
+
from torre.analysis import scoring
|
|
22
|
+
SIDES = ('you', 'bot')
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def _pair(ev):
|
|
26
|
+
"""An engine_lines entry or a [cp, mate] pair as (cp, mate), mate taking the place of cp."""
|
|
27
|
+
if isinstance(ev, dict):
|
|
28
|
+
return (None, ev['mate']) if ev.get('mate') is not None else (ev.get('cp'), None)
|
|
29
|
+
return tuple(ev)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def report(initial_fen, ucis, player_color, evals, notes=()):
|
|
33
|
+
"""The card for a finished game. `evals` holds len(ucis) + 1 White-POV evals; `notes` the
|
|
34
|
+
bot's own record of its moves ({ply, slip, elo}), as the play page saved them."""
|
|
35
|
+
board = chess.Board(initial_fen)
|
|
36
|
+
evals = [_pair(ev) for ev in evals]
|
|
37
|
+
if len(evals) != len(ucis) + 1:
|
|
38
|
+
raise ValueError('The review does not cover the whole game.')
|
|
39
|
+
you = chess.WHITE if player_color == 'white' else chess.BLACK
|
|
40
|
+
win = [game_stats.win_pct(*ev) for ev in evals]
|
|
41
|
+
weights = game_stats.volatility_weights(win)
|
|
42
|
+
by_ply = {n['ply']: n for n in notes if isinstance(n, dict) and isinstance(n.get('ply'), int)}
|
|
43
|
+
rows = {phase: {side: [] for side in SIDES} for phase in game_stats.PHASES}
|
|
44
|
+
slips, ratings, phase, worst = [], {}, None, None
|
|
45
|
+
for i, uci in enumerate(ucis):
|
|
46
|
+
move = chess.Move.from_uci(uci)
|
|
47
|
+
phase = game_stats.position_phase(board, phase)
|
|
48
|
+
mover = board.turn
|
|
49
|
+
side = 'you' if mover == you else 'bot'
|
|
50
|
+
before, after = (win[i], win[i + 1]) if mover == chess.WHITE else (100 - win[i], 100 - win[i + 1])
|
|
51
|
+
judged = scoring.judge(evals[i], evals[i + 1], mover == chess.WHITE)
|
|
52
|
+
rows[phase][side].append({'accuracy': game_stats.move_accuracy(before, after), 'weight': weights[i], 'class': judged})
|
|
53
|
+
if side == 'you' and judged in ('mistake', 'blunder') and (worst is None or before - after > worst['drop']):
|
|
54
|
+
worst = {'ply': i + 1, 'san': board.san(move), 'class': judged, 'fen': board.fen(), 'drop': before - after}
|
|
55
|
+
note = by_ply.get(i + 1)
|
|
56
|
+
if side == 'bot' and note:
|
|
57
|
+
if isinstance(note.get('elo'), int):
|
|
58
|
+
ratings.setdefault(phase, set()).add(note['elo'])
|
|
59
|
+
if note.get('slip'):
|
|
60
|
+
slips.append({'ply': i + 1, 'san': board.san(move), 'phase': phase, 'class': judged})
|
|
61
|
+
board.push(move)
|
|
62
|
+
|
|
63
|
+
def summary(moves):
|
|
64
|
+
if not moves:
|
|
65
|
+
return None
|
|
66
|
+
accuracy = game_stats.aggregate_accuracy([(m['accuracy'], m['weight']) for m in moves])
|
|
67
|
+
return {'moves': len(moves), 'accuracy': round(accuracy, 1),
|
|
68
|
+
**{e: sum(m['class'] == e for m in moves) for e in game_stats.ERRORS}}
|
|
69
|
+
|
|
70
|
+
overall = {side: summary([m for phase in game_stats.PHASES for m in rows[phase][side]]) for side in SIDES}
|
|
71
|
+
return {
|
|
72
|
+
'phases': {phase: {side: summary(rows[phase][side]) for side in SIDES} for phase in game_stats.PHASES},
|
|
73
|
+
'overall': overall,
|
|
74
|
+
'slips': slips,
|
|
75
|
+
# A slip that came out fine (no error by the judgment rules) is luck, not a stronger bot.
|
|
76
|
+
'luckySlips': sum(1 for s in slips if s['class'] is None),
|
|
77
|
+
'ratings': {phase: sorted(values) for phase, values in ratings.items()},
|
|
78
|
+
'retry': {k: v for k, v in worst.items() if k != 'drop'} if worst else None,
|
|
79
|
+
}
|