{
  "id": 567106,
  "title": "[9th Place Solution] Cfish + Simple SPSA",
  "content": "<p>I'm sharing the solution that earned 9th place. The final code is publicly available at the following link:</p>\n<p><a href=\"https://github.com/ymgaq/Cfish_kaggle\" target=\"_blank\">https://github.com/ymgaq/Cfish_kaggle</a></p>\n<h1>Selecting the Base Program</h1>\n<p>After testing multiple chess engines, I finally chose <a href=\"https://github.com/syzygy1/Cfish\" target=\"_blank\">Cfish</a>.</p>\n<p>As noted by other top participants, Cfish has the advantage of lower memory consumption due to glibc compared to C++-based engines. Additionally, in Kaggle's single-core environment, it appeared to slightly outperform Stockfish in search speed.</p>\n<h1>Memory Optimization</h1>\n<ul>\n<li>Removed NNUE, relying exclusively on HCE evaluations.</li>\n<li>Reduced the Transposition Table size to 1MB.</li>\n<li>Counter Move History:<ul>\n<li>Reduced branching by merging conditions (inCheck, CaptureOrPromotion) (1/4 reduction).</li>\n<li>Changed Counter Move History indexing from pieces to types of pieces (1/2 reduction).</li></ul></li>\n<li>Reduced Material Table and Pawn Hash Table sizes to 1024.</li>\n</ul>\n<p>These adjustments resulted in memory usage of around 4MB.</p>\n<h1>Binary Size Compression</h1>\n<ul>\n<li>Removed unnecessary functions and classes (NNUE, Bench, TBProbe, PolyBook, NUMA, etc.).</li>\n<li>Removed unnecessary UCI options.</li>\n<li>Optimized build commands:<ul>\n<li>make build -j ARCH=x86-64-bmi2 EXTRACFLAGS=\"-ffunction-sections -fdata-sections\" EXTRALDFLAGS=\"-Wl,--gc-sections\"</li></ul></li>\n<li>Used <code>strip</code> command.</li>\n<li>Compressed binary with <code>upx --lzma</code>.</li>\n</ul>\n<p>These reductions allowed the binary size to remain under 64KB, even with O3 optimization instead of Os.</p>\n<h1>Enhanced Search</h1>\n<p>Cfish’s search initially matched Stockfish13 but was modified to resemble Stockfish16’s HCE search methods and parameters. This modification improved the engine by roughly +30 elo compared to the original search implementation. By using O3 instead of Os optimization, the average search speed increased by approximately 8-9% in Kaggle's CPU environment.</p>\n<h1>SPSA</h1>\n<p>Chess engines commonly use a powerful black-box optimization method called <a href=\"http://www.jhuapl.edu/spsa/PDF-SPSA/Spall_Implementation_of_the_Simultaneous.PDF\" target=\"_blank\">SPSA</a> (Simultaneous Perturbation Stochastic Approximation).</p>\n<p>Simply put, two versions of the engine with parameters perturbed by +δ and -δ compete against each other, and the winning version’s adjustments are accepted. By randomly perturbing and testing all parameters simultaneously and repeating matches, the optimal parameters are identified.</p>\n<p>While robust SPSA libraries such as <a href=\"https://github.com/official-stockfish/fishtest\" target=\"_blank\">fishtest</a> or <a href=\"https://github.com/AndyGrant/OpenBench\" target=\"_blank\">OpenBench</a> exist, I opted for a simple custom SPSA script optimized for a single local machine. <a href=\"https://github.com/ymgaq/Cfish_kaggle/blob/kaggle_update_params_o3/scripts/spsa.py\" target=\"_blank\">My script</a>, around 400 lines, was based on literature and an available <a href=\"https://github.com/zamar/spsa\" target=\"_blank\">Perl implementation</a>. It utilizes cutechess-cli and runs SPSA tests concurrently on multiple threads, adopting the competition’s 2000-opening book and 10-second time controls.</p>\n<p>This straightforward approach yielded approximately +30 elo improvements, pushing my solution to a Gold Medal.</p>\n<p>Considering the randomness of the LB, I submitted two identical binaries as my final submission.</p>\n<h1>Other Attempts (Not Adopted)</h1>\n<ul>\n<li>Tested Ethereal according to publicly shared notebooks.</li>\n<li>Evaluated memory optimization strategies for all HCE versions of Stockfish 6 to 16. Migrated to Cfish to meet the 5MB memory constraint.</li>\n<li>Attempted training a (768x2)-10-1 NNUE network using bullet, but unfortunately, I could not surpass the performance of HCE.</li>\n<li>SPSA adjustments for time control parameters were not effective in LB testing, hence not adopted.</li>\n</ul>\n<p>I found other winning solutions utilizing NNUE and neural networks extremely intriguing and slightly regret limiting my approach exclusively to HCE. Though this was my first experience developing a chess engine, optimizing within constraints akin to embedded systems proved highly engaging. I extend my sincere gratitude to the competition organizers and fellow participants for their extensive and valuable contributions.</p>",
  "comments": [],
  "comment_count": 0,
  "messages": [
    {
      "id": 3144482,
      "postDate": "2025-03-08T12:34:13.773Z",
      "content": "<p>I'm sharing the solution that earned 9th place. The final code is publicly available at the following link:</p>\n<p><a href=\"https://github.com/ymgaq/Cfish_kaggle\" target=\"_blank\">https://github.com/ymgaq/Cfish_kaggle</a></p>\n<h1>Selecting the Base Program</h1>\n<p>After testing multiple chess engines, I finally chose <a href=\"https://github.com/syzygy1/Cfish\" target=\"_blank\">Cfish</a>.</p>\n<p>As noted by other top participants, Cfish has the advantage of lower memory consumption due to glibc compared to C++-based engines. Additionally, in Kaggle's single-core environment, it appeared to slightly outperform Stockfish in search speed.</p>\n<h1>Memory Optimization</h1>\n<ul>\n<li>Removed NNUE, relying exclusively on HCE evaluations.</li>\n<li>Reduced the Transposition Table size to 1MB.</li>\n<li>Counter Move History:<ul>\n<li>Reduced branching by merging conditions (inCheck, CaptureOrPromotion) (1/4 reduction).</li>\n<li>Changed Counter Move History indexing from pieces to types of pieces (1/2 reduction).</li></ul></li>\n<li>Reduced Material Table and Pawn Hash Table sizes to 1024.</li>\n</ul>\n<p>These adjustments resulted in memory usage of around 4MB.</p>\n<h1>Binary Size Compression</h1>\n<ul>\n<li>Removed unnecessary functions and classes (NNUE, Bench, TBProbe, PolyBook, NUMA, etc.).</li>\n<li>Removed unnecessary UCI options.</li>\n<li>Optimized build commands:<ul>\n<li>make build -j ARCH=x86-64-bmi2 EXTRACFLAGS=\"-ffunction-sections -fdata-sections\" EXTRALDFLAGS=\"-Wl,--gc-sections\"</li></ul></li>\n<li>Used <code>strip</code> command.</li>\n<li>Compressed binary with <code>upx --lzma</code>.</li>\n</ul>\n<p>These reductions allowed the binary size to remain under 64KB, even with O3 optimization instead of Os.</p>\n<h1>Enhanced Search</h1>\n<p>Cfish’s search initially matched Stockfish13 but was modified to resemble Stockfish16’s HCE search methods and parameters. This modification improved the engine by roughly +30 elo compared to the original search implementation. By using O3 instead of Os optimization, the average search speed increased by approximately 8-9% in Kaggle's CPU environment.</p>\n<h1>SPSA</h1>\n<p>Chess engines commonly use a powerful black-box optimization method called <a href=\"http://www.jhuapl.edu/spsa/PDF-SPSA/Spall_Implementation_of_the_Simultaneous.PDF\" target=\"_blank\">SPSA</a> (Simultaneous Perturbation Stochastic Approximation).</p>\n<p>Simply put, two versions of the engine with parameters perturbed by +δ and -δ compete against each other, and the winning version’s adjustments are accepted. By randomly perturbing and testing all parameters simultaneously and repeating matches, the optimal parameters are identified.</p>\n<p>While robust SPSA libraries such as <a href=\"https://github.com/official-stockfish/fishtest\" target=\"_blank\">fishtest</a> or <a href=\"https://github.com/AndyGrant/OpenBench\" target=\"_blank\">OpenBench</a> exist, I opted for a simple custom SPSA script optimized for a single local machine. <a href=\"https://github.com/ymgaq/Cfish_kaggle/blob/kaggle_update_params_o3/scripts/spsa.py\" target=\"_blank\">My script</a>, around 400 lines, was based on literature and an available <a href=\"https://github.com/zamar/spsa\" target=\"_blank\">Perl implementation</a>. It utilizes cutechess-cli and runs SPSA tests concurrently on multiple threads, adopting the competition’s 2000-opening book and 10-second time controls.</p>\n<p>This straightforward approach yielded approximately +30 elo improvements, pushing my solution to a Gold Medal.</p>\n<p>Considering the randomness of the LB, I submitted two identical binaries as my final submission.</p>\n<h1>Other Attempts (Not Adopted)</h1>\n<ul>\n<li>Tested Ethereal according to publicly shared notebooks.</li>\n<li>Evaluated memory optimization strategies for all HCE versions of Stockfish 6 to 16. Migrated to Cfish to meet the 5MB memory constraint.</li>\n<li>Attempted training a (768x2)-10-1 NNUE network using bullet, but unfortunately, I could not surpass the performance of HCE.</li>\n<li>SPSA adjustments for time control parameters were not effective in LB testing, hence not adopted.</li>\n</ul>\n<p>I found other winning solutions utilizing NNUE and neural networks extremely intriguing and slightly regret limiting my approach exclusively to HCE. Though this was my first experience developing a chess engine, optimizing within constraints akin to embedded systems proved highly engaging. I extend my sincere gratitude to the competition organizers and fellow participants for their extensive and valuable contributions.</p>",
      "rawMarkdown": "I'm sharing the solution that earned 9th place. The final code is publicly available at the following link:\n\n[https://github.com/ymgaq/Cfish_kaggle](https://github.com/ymgaq/Cfish_kaggle)\n\n# Selecting the Base Program\n\nAfter testing multiple chess engines, I finally chose [Cfish](https://github.com/syzygy1/Cfish).\n\nAs noted by other top participants, Cfish has the advantage of lower memory consumption due to glibc compared to C++-based engines. Additionally, in Kaggle's single-core environment, it appeared to slightly outperform Stockfish in search speed.\n\n# Memory Optimization\n- Removed NNUE, relying exclusively on HCE evaluations.\n- Reduced the Transposition Table size to 1MB.\n- Counter Move History:\n  - Reduced branching by merging conditions (inCheck, CaptureOrPromotion) (1/4 reduction).\n  - Changed Counter Move History indexing from pieces to types of pieces (1/2 reduction).\n- Reduced Material Table and Pawn Hash Table sizes to 1024.\n\nThese adjustments resulted in memory usage of around 4MB.\n\n# Binary Size Compression\n- Removed unnecessary functions and classes (NNUE, Bench, TBProbe, PolyBook, NUMA, etc.).\n- Removed unnecessary UCI options.\n- Optimized build commands:\n  - make build -j ARCH=x86-64-bmi2 EXTRACFLAGS=\"-ffunction-sections -fdata-sections\" EXTRALDFLAGS=\"-Wl,--gc-sections\"\n- Used `strip` command.\n- Compressed binary with `upx --lzma`.\n\nThese reductions allowed the binary size to remain under 64KB, even with O3 optimization instead of Os.\n\n# Enhanced Search\n\nCfish’s search initially matched Stockfish13 but was modified to resemble Stockfish16’s HCE search methods and parameters. This modification improved the engine by roughly +30 elo compared to the original search implementation. By using O3 instead of Os optimization, the average search speed increased by approximately 8-9% in Kaggle's CPU environment.\n\n# SPSA\n\nChess engines commonly use a powerful black-box optimization method called [SPSA](http://www.jhuapl.edu/spsa/PDF-SPSA/Spall_Implementation_of_the_Simultaneous.PDF) (Simultaneous Perturbation Stochastic Approximation).\n\nSimply put, two versions of the engine with parameters perturbed by +δ and -δ compete against each other, and the winning version’s adjustments are accepted. By randomly perturbing and testing all parameters simultaneously and repeating matches, the optimal parameters are identified.\n\nWhile robust SPSA libraries such as [fishtest](https://github.com/official-stockfish/fishtest) or [OpenBench](https://github.com/AndyGrant/OpenBench) exist, I opted for a simple custom SPSA script optimized for a single local machine. [My script](https://github.com/ymgaq/Cfish_kaggle/blob/kaggle_update_params_o3/scripts/spsa.py), around 400 lines, was based on literature and an available [Perl implementation](https://github.com/zamar/spsa). It utilizes cutechess-cli and runs SPSA tests concurrently on multiple threads, adopting the competition’s 2000-opening book and 10-second time controls.\n\nThis straightforward approach yielded approximately +30 elo improvements, pushing my solution to a Gold Medal.\n\nConsidering the randomness of the LB, I submitted two identical binaries as my final submission.\n\n# Other Attempts (Not Adopted)\n- Tested Ethereal according to publicly shared notebooks.\n- Evaluated memory optimization strategies for all HCE versions of Stockfish 6 to 16. Migrated to Cfish to meet the 5MB memory constraint.\n- Attempted training a (768x2)-10-1 NNUE network using bullet, but unfortunately, I could not surpass the performance of HCE.\n- SPSA adjustments for time control parameters were not effective in LB testing, hence not adopted.\n\n\nI found other winning solutions utilizing NNUE and neural networks extremely intriguing and slightly regret limiting my approach exclusively to HCE. Though this was my first experience developing a chess engine, optimizing within constraints akin to embedded systems proved highly engaging. I extend my sincere gratitude to the competition organizers and fellow participants for their extensive and valuable contributions.",
      "votes": 18
    }
  ],
  "complete": true,
  "source": "live",
  "fetched_at": "2026-10-20T08:26:17Z",
  "competition_id": 86524,
  "competition_slug": "fide-google-efficiency-chess-ai-challenge",
  "team_id": 13173394,
  "solution_url": "/competitions/fide-google-efficiency-chess-ai-challenge/writeups/ymg-aq-9th-place-solution-cfish-simple-spsa"
}