stockfish.js icon indicating copy to clipboard operation
stockfish.js copied to clipboard

How to play a game from a certain FEN position?

Open narciso2019 opened this issue 5 years ago • 2 comments

Dear Nathan,

Would you please take a look at my clumsy implementation of your stockfish.js example: https://chesswise.defiantchris.com/stockfish I cannot feed a FEN position of choice to start a game from. I looked up the documentation suggesting: ################################# var stockfish = new Worker("stockfish.js"); stockfish.onmessage = function onmessage(event) { console.log(event.data); }; var fenString = "r1bqk1nr/pppp1ppp/2n5/8/1bBpP3/5N2/PPP2PPP/RNBQK2R w KQkq - 2 5" // start UCI stockfish.postMessage("uci"); // start new game stockfish.postMessage("ucinewgame"); // set new game position stockfish.postMessage("position fen " + fenString); // start search stockfish.postMessage("go depth 10"); ################################ But it did not seem to have worked for me. I do not know JS that well and have a feeling I do not understand the code placing mine in the wrong place. Could you possibly instruct me on that preferably with a simple index.html file.

My goal is to put up interactive diagrams with randomly generated FEN positions to practice different types of endgames. I wrote code in Perl to generate FENs and put them up on the website using Apronus.com server (you can take a look at it: https://chesswise.defiantchris.com/cgi-bin/20_chessbase_endgame_bishop&knight_random.pl). However, I would rather use stockfish.js on my website instead of Apronus server.

Thank you in advance for any help you can offer.

Best wishes,

Narciso

narciso2019 avatar Sep 21 '20 13:09 narciso2019

Just joining in to say +1, I've been trying to figure out how to use this in my project for some time now and cannot figure it out. Is there some function that takes in a FEN, PGN, or chess.js game object (and a depth/difficulty) and returns the best move it finds? I checked the source code and the example website and cannot find any function that does this that I can call

justingolden21 avatar Mar 03 '21 18:03 justingolden21

I was working on the same thing and figured it out -- it's by no means obvious and within the stockfish example there are lots of little trips and pitfalls. I found a couple questions online and thought I'd give them some answers.

So -- this answer assumes working with the example code found here: https://github.com/nmrugg/stockfish.js/tree/Stockfish11/example.

There are two major modifications that need to happen - first in the index.html file and second in enginegame.js.

First we'll define a helper function which will make it easy to work with the url "search" as it's called:

function searchToObject() {      
  var pairs = window.location.search.substring(1).split("&"),      
    obj = {},      
    pair,      
    i;      
            
  for ( i in pairs ) {      
    if ( pairs[i] === "" ) continue;      
          
    pair = pairs[i].split("=");      
    obj[ decodeURIComponent( pair[0] ) ] = decodeURIComponent( pair[1] );      
  }      
          
  return obj;      
}

For ease I just placed that function in both files, within index.html it's at the beginning of the script tag, in enginegame.js it's the very first line. Also btw, I certainly pilfered that from stackoverflow, but I can't seem to find that answer any more, rats.

In index.html the newGame function wants to look like this:

newGame = function newGame() {        
  var baseTime = parseFloat($('#timeBase').val()) * 60;        
  var inc = parseFloat($('#timeInc').val());        
  var skill = parseInt($('#skillLevel').val());        
 
  game.reset();        
 
  let search = searchToObject();        
 
  if (search.player) {        
    game.setPlayerColor(search.player)        
  } else {        
    game.setPlayerColor($('#color-white').hasClass('active') ? 'white' : 'black');        
  }        
 
  if (search.fen) {        
    game.game.load(search.fen);        
    game.board.position(game.game.fen());        
  }        
 
  game.setTime(baseTime, inc);        
  game.setSkillLevel(skill);        
  game.setDisplayScore($('#showScore').is(':checked'));        
 
  game.start();        
}

Note the game.game and game.board -- those need to be added in enginegame.js where it's returning an object. If I were writing this I would have done it differently, but I didn't have the patience to rename things.

Next up in enginegame.js we need to adjust prepareMove.

function prepareMove() {    
    stopClock();    
    $('#pgn').text(game.pgn());    
    board.position(game.fen());    
    updateClock();    
    var turn = game.turn() == 'w' ? 'white' : 'black';    
    if (!game.game_over()) {    
        if (turn != playerColor) {    
            let search = searchToObject();    
            if (search.fen) {    
                uciCmd('position fen ' + search.fen + ' moves ' + get_moves());    
            } else {    
                uciCmd('position startpos moves' + get_moves());    
                uciCmd('position startpos moves' + get_moves(), evaler);    
            }    
            evaluation_el.textContent = "";    
            uciCmd("eval", evaler);    
 
            if (time && time.wtime) {    
                uciCmd("go " + (time.depth ? "depth " + time.depth : "") + " wtime " + time.wtime + " winc " + time.winc + " btime " + time.btime + " binc " + time.binc);    
            } else {    
                uciCmd("go " + (time.depth ? "depth " + time.depth : ""));    
            }    
            isEngineRunning = true;    
        }    
        if (game.history().length >= 2 && !time.depth && !time.nodes) {    
            startClock();    
        }    
    }    
}

See, the trick is that if ever there was a fen string to start the game, every subsequent position call needs to be different. I think that's probably what's tripping most people up - that's definitely what got me.

What helped things click for me was reading through the UCI documentation. Before that my board was in some crazy infinite loop.

Also one weird but critical bit I stumbled onto was the game.game.load(<fen string>) function call in the index.html file. I can't find any documentation for that. I don't even remember how I found it. But there it is!

aaronik avatar Jan 26 '23 02:01 aaronik