Answers.Body: <p>Use engine.play because you are using skill level, note use info parameter to get the score. You don't need to worry about the hash table because you quit the engine for every skill levels.</p>
  <h2>Method 1</h2>
  <h5>Code</h5>
  <pre><code>import chess.engine
  import logging
  
  # Uncomment to see the log in console.
  # logging.basicConfig(level=logging.DEBUG)
  
  
  stockfishpath = 'sf15.exe'
  fens = [
      'r4rk1/5pp1/5q2/p2n2p1/2R3P1/7P/P1Q2P2/2R2BK1 w - - 0 1',
      'rn1qkbnr/pp2pppp/2p5/3p1b2/3P4/4PN2/PPP2PPP/RNBQKB1R w KQkq - 0 1'
  ]
  skill_levels=[2,10,20]
  analysis_time_sec = 2
  hash_mb = 128
  
  for fen in fens:
      print(f'FEN: {fen}')
  
      for s in skill_levels:
          engine = chess.engine.SimpleEngine.popen_uci(stockfishpath)
          engine.configure({&quot;Skill Level&quot;: s})
          engine.configure({&quot;Hash&quot;: hash_mb})
          board = chess.Board(fen)
          result = engine.play(board, chess.engine.Limit(time=analysis_time_sec), info=chess.engine.INFO_SCORE)
          engine.quit()
  
          bestmove = board.san(result.move)
          score_cp = result.info['score'].relative.score(mate_score=32000)
          score_mate = result.info['score'].relative.mate()
  
          print(f'skill level: {s}')
          print(f'score cp: {score_cp}')
          print(f'score mate: {score_mate}')
          print(f'bestmove: {bestmove}')
          print()
  
  </code></pre>
  <h5>Output</h5>
  <pre><code>FEN: r4rk1/5pp1/5q2/p2n2p1/2R3P1/7P/P1Q2P2/2R2BK1 w - - 0 1
  skill level: 2
  score cp: 57
  score mate: None
  bestmove: Rd1
  
  skill level: 10
  score cp: 57
  score mate: None
  bestmove: Rc6
  
  skill level: 20
  score cp: 45
  score mate: None
  bestmove: Rc6
  
  FEN: rn1qkbnr/pp2pppp/2p5/3p1b2/3P4/4PN2/PPP2PPP/RNBQKB1R w KQkq - 0 1
  skill level: 2
  score cp: 34
  score mate: None
  bestmove: Nh4
  
  skill level: 10
  score cp: 34
  score mate: None
  bestmove: Bd3
  
  skill level: 20
  score cp: 23
  score mate: None
  bestmove: Bd3
  </code></pre>
  <p>Regarding your second question do the following:</p>
  <ol>
  <li>Run the engine</li>
  <li>Set engine options you like, e.g<br />
  <code>setoption name Hash value 128</code></li>
  <li>Send the command<br />
  <code>go movetime 1000</code><br />
  that will search the engine for 1000 ms or 1 sec.
  Sample output:</li>
  </ol>
  <pre><code>info string NNUE evaluation using nn-6877cd24400e.nnue enabled
  info depth 1 seldepth 1 multipv 1 score cp 30 nodes 20 nps 10000 tbhits 0 time 2 pv e2e4
  info depth 2 seldepth 2 multipv 1 score cp 84 nodes 45 nps 22500 tbhits 0 time 2 pv e2e4 a7a6
  info depth 3 seldepth 3 multipv 1 score cp 37 nodes 191 nps 63666 tbhits 0 time 3 pv c2c4 a7a6 e2e4
  info depth 4 seldepth 4 multipv 1 score cp 158 nodes 264 nps 88000 tbhits 0 time 3 pv c2c4
  info depth 5 seldepth 6 multipv 1 score cp 49 nodes 1449 nps 241500 tbhits 0 time 6 pv e2e4 d7d5 e4d5 d8d5 d2d4
  info depth 6 seldepth 6 multipv 1 score cp 36 nodes 3954 nps 395400 tbhits 0 time 10 pv e2e4 c7c5 c2c3 b8c6
  info depth 7 seldepth 9 multipv 1 score cp 37 nodes 6269 nps 447785 tbhits 0 time 14 pv e2e4 c7c5 g1f3 b8c6 d2d4 c5d4 f3d4 g8f6
  info depth 8 seldepth 12 multipv 1 score cp 30 nodes 11049 nps 502227 tbhits 0 time 22 pv e2e4 e7e5 g1f3 b8c6 d2d4 e5d4 f3d4 g8f6 d4c6 b7c6
  info depth 9 seldepth 13 multipv 1 score cp 30 nodes 15347 nps 495064 tbhits 0 time 31 pv e2e4 e7e6 d2d4 d7d5 b1d2 g8f6 e4e5 f6d7 c2c3 c7c5 g1f3
  info depth 10 seldepth 14 multipv 1 score cp 37 nodes 23259 nps 540906 tbhits 0 time 43 pv e2e4 c7c5 g1f3 d7d6 f1b5 c8d7 b5d7 d8d7 e1g1
  info depth 11 seldepth 14 multipv 1 score cp 45 nodes 40064 nps 556444 tbhits 0 time 72 pv e2e4 c7c5 g1f3 e7e6 g2g3 d7d6 d2d4 c5d4 f3d4 g8f6
  info depth 12 seldepth 15 multipv 1 score cp 38 nodes 57767 nps 571950 tbhits 0 time 101 pv e2e4 c7c5 g1f3 b8c6 f1b5 c6d4 b5e2 e7e6 f3d4 c5d4 e1g1 a7a6
  info depth 13 seldepth 15 multipv 1 score cp 46 nodes 97551 nps 602166 tbhits 0 time 162 pv d2d4 d7d5 g1f3 g8f6 c2c4 e7e6 g2g3 f8e7 f1g2 e8g8 e1g1 d5c4 b1d2
  info depth 14 seldepth 20 multipv 1 score cp 38 nodes 152736 nps 606095 tbhits 0 time 252 pv d2d4 d7d5 g1f3 g8f6 c2c4 c7c6 b1c3 g7g6 e2e3 f8g7 f1e2 e8g8 e1g1 d5c4 e2c4
  info depth 15 seldepth 17 multipv 1 score cp 43 nodes 214622 nps 614962 tbhits 0 time 349 pv e2e4 c7c5 g1f3 d7d6 f1b5 c8d7 b5d7 b8d7 e1g1 g7g6 f1e1 g8f6 c2c3 f8g7 d2d4 c5d4
  info depth 16 seldepth 23 multipv 1 score cp 33 nodes 413957 nps 619696 tbhits 0 time 668 pv e2e4 c7c5 g1f3 d7d6 f1b5 b8d7 e1g1 a7a6 b5d7 c8d7 f1e1 e7e5 c2c3 g8e7 d2d4 e7g6 d4e5 d6e5 c3c4 f8e7 b1c3 e8g8 c3d5
  info depth 17 seldepth 24 multipv 1 score cp 35 nodes 525134 nps 621460 tbhits 0 time 845 pv e2e4 c7c5 c2c3 g8f6 e4e5 f6d5 d2d4 c5d4 c3d4 b8c6 g1f3 e7e6 b1c3 d7d6 f1d3 d6e5 d4e5 f8b4
  info depth 18 seldepth 24 multipv 1 score cp 35 nodes 619893 nps 618655 hashfull 37 tbhits 0 time 1002 pv e2e4 c7c5 c2c3 g8f6 e4e5 f6d5 d2d4 c5d4 c3d4 b8c6 g1f3 e7e6 b1c3 d7d6 f1d3 d6e5 d4e5 f8b4
  bestmove e2e4 ponder c7c5
  </code></pre>
  <p>So in my PC the depth reached in single thread for 1 sec search is 18.</p>
  <h2>Method 2</h2>
  <h5>Code</h5>
  <pre><code>&quot;&quot;&quot;
  Evaluate positions with skill levels and get the correct score
  of the move corresponding to the skill level. Also Applicable
  to uci engines that does not show correct score for the move when playing suboptimal moves.
  &quot;&quot;&quot;
  
  import chess.engine
  # import logging
  
  # Uncomment to see the log in console.
  # logging.basicConfig(level=logging.DEBUG)
  
  
  def analyze_position(stockfishpath, board, root_moves=None, skill_level=20, hash_mb=16, time_sec=1):
      &quot;&quot;&quot;
      Analyze board position and return bestmove, score_cp and score_mate.
      &quot;&quot;&quot;
      engine = chess.engine.SimpleEngine.popen_uci(stockfishpath)
      engine.configure({&quot;Skill Level&quot;: skill_level})
      engine.configure({&quot;Hash&quot;: hash_mb})
      result = engine.play(board, chess.engine.Limit(time=time_sec), info=chess.engine.INFO_SCORE, root_moves=root_moves)
      engine.quit()
  
      score_cp = result.info['score'].relative.score(mate_score=32000)
      score_mate = result.info['score'].relative.mate()
      bestmove = result.move
  
      return bestmove, score_cp, score_mate
  
  
  def main():
      stockfishpath = 'sf15.exe'
      fens = [
          '3K4/8/8/p2k4/pp1B4/N5N1/P2Q4/8 w - - 0 1'
      ]
      skill_levels=[2,10,20]
      analysis_time_sec = 1
      hash_mb = 128
  
      for fen in fens:
          print(f'FEN: {fen}')
  
          for s in skill_levels:
              board = chess.Board(fen)
              bestmove, score_cp, score_mate = analyze_position(
                  stockfishpath, board, root_moves=None, skill_level=s,
                  hash_mb=hash_mb, time_sec=analysis_time_sec)
  
              # Restart the engine to get the actual score of the bestmove returned when skill level is not best.
              # This is because the current stockfish (v15) does not report the correct score based on the bestmove it returned.
              # We will use the searchmoves &lt;move&gt; command to restrict the engine to search only this move.
              if s &lt; 20:
                  # We will set the max skill level because we are after on the correct score of the given move.
                  max_s = 20
                  root_move = [bestmove]
                  bestmove, score_cp, score_mate = analyze_position(
                      stockfishpath, board, root_moves=root_move, skill_level=max_s,
                      hash_mb=hash_mb, time_sec=analysis_time_sec)
  
              print(f'skill level: {s}')
              print(f'score cp: {score_cp}')
              print(f'score mate: {score_mate}')
              print(f'bestmove: {board.san(bestmove)}')
              print()
  
  
  if __name__ == '__main__':
      main()
  </code></pre>
  <h5>Output</h5>
  <pre><code>FEN: 3K4/8/8/p2k4/pp1B4/N5N1/P2Q4/8 w - - 0 1
  skill level: 2
  score cp: 31996
  score mate: 4
  bestmove: Kc7
  
  skill level: 10
  score cp: 31995
  score mate: 5
  bestmove: Bb6+
  
  skill level: 20
  score cp: 31997
  score mate: 3
  bestmove: Qd1
  </code></pre>
  
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2022-05-30T18:49:08.647Z
Answers.Text: thank you very much for your reply. That's exactly what I needed.
  But I have a problem, probably I cannot understand the result properly.
  Consider this position
  
  `'3K4/8/8/p2k4/pp1B4/N5N1/P2Q4/8 w - - 0 1'`
  this is what I get
  `FEN: 3K4/8/8/p2k4/pp1B4/N5N1/P2Q4/8 w - - 0 1
  skill level: 2
  score cp: 31997
  score mate: 3
  bestmove: Kc7
  `
  
  why at skill level 2, the socore mate is 3 but the best move is Kc7 which does not leat to a mate in 3? Sumilarly for skill level = 10
Answers.UserDisplayName: Giorgio
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2022-05-30T23:17:50.683Z
Answers.Text: The engine suggests a weak move but the score it sends is not based from the suggested move this is a bug in the engine. Do you also want an accurate score based on the given move when skill level is not best? I will add new code later to my answer to determine accurate score of the move sent by the engine when skill level is not best.
Answers.UserDisplayName: ferdy
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2022-05-31T00:08:42.807Z
Answers.Text: Added method 2 in the answer to get the correct score of the bestmove returned when skill level is not best or 20.
Answers.UserDisplayName: ferdy
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2022-06-01T16:22:38.183Z
Answers.Text: it's perfect! thank you!
  Where does the 'mate_score=32000' come from?
Answers.UserDisplayName: Giorgio
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2022-06-01T16:42:03.153Z
Answers.Text: 32k is from sf source code.
Answers.UserDisplayName: ferdy
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2022-05-27T11:38:17.210Z
Answers.LastActivityDate: 2022-05-31T22:52:22.960Z
Answers.LastEditDate: 2022-05-31T22:52:22.960Z
Answers.UserDisplayName: ferdy
Body: <p>I want to make some statistics by analyzing the same position, multiple times, at different levels of Stockfish.
  Precisely, I would like Stockfish to analyze a specific position at different <code>&quot;Skill Level&quot;</code> but what I've noticed is that there is the &quot;hash table&quot; which keeps in memory the previous analyses.
  Somewhere I've found that if I specify the parameter <code>game=object()</code> the hash table has been reset but I'm not sure about that. What I want is that the second analysis is completely independent from the first one.</p>
  <pre><code>skill_levels=[2,10,20]
  for i in range(len(skill_levels)):
      engine = chess.engine.SimpleEngine.popen_uci(stockfishpath)
      engine.configure({&quot;Skill Level&quot;: skill_levels[i]})
      board = chess.Board(fens[f])
      dummy_move = engine.play(board, chess.engine.Limit(time=0.1), game=object()).move
      result = engine.analyse(board, chess.engine.Limit(time=1), game=object(),root_moves=[dummy_move])
      engine.quit()
  </code></pre>
  <p>How do you do this properly?</p>
  <p>Second question: I suppose that limiting the engine on time <code>chess.engine.Limit(time=1)</code> I will get different results depending on the specs of the PC where Stockfish is running. How do I make the results somehow independent of that?
  Considering to use an average pc (i5 8th generation, 16gb of ram) which depth should I chose <code>chess.engine.Limit(depth=20)</code> the get more or less the same results of <code>chess.engine.Limit(time=1)</code>?</p>
  
Comments.ContentLicense: CC BY-SA 4.0
Comments.CreationDate: 2022-05-27T01:18:21.707Z
Comments.Text: When you analyze the position by skill level what search info you would like to get? Is it the bestmove, the evaluation, nodes, nps?
Comments.UserDisplayName: ferdy
Comments.ContentLicense: CC BY-SA 4.0
Comments.CreationDate: 2022-05-27T07:23:03.627Z
Comments.Text: the evaluation, precisely in Cp or Mate. But is that important?
Comments.UserDisplayName: Giorgio
Comments.ContentLicense: CC BY-SA 4.0
Comments.CreationDate: 2022-05-27T08:11:56.350Z
Comments.Text: It is important because of your posted code. In that code you did not extract the score. I will post answer later on how you will get the score.
Comments.UserDisplayName: ferdy
Tags: <engines><stockfish><computer-chess><python>
Title: How do you nerf Stockfish to analyze the same position multiple times?
ThreadText: # How do you nerf Stockfish to analyze the same position multiple times?
  
  (Asked by: username_0 on 2022-05-26)
  
  I want to make some statistics by analyzing the same position, multiple times, at different levels of Stockfish.
  Precisely, I would like Stockfish to analyze a specific position at different "Skill Level" but what I've noticed is that there is the "hash table" which keeps in memory the previous analyses.
  Somewhere I've found that if I specify the parameter game=object() the hash table has been reset but I'm not sure about that. What I want is that the second analysis is completely independent from the first one.
  skill_levels=[2,10,20]
  for i in range(len(skill_levels)):
      engine = chess.engine.SimpleEngine.popen_uci(stockfishpath)
      engine.configure({"Skill Level": skill_levels[i]})
      board = chess.Board(fens[f])
      dummy_move = engine.play(board, chess.engine.Limit(time=0.1), game=object()).move
      result = engine.analyse(board, chess.engine.Limit(time=1), game=object(),root_moves=[dummy_move])
      engine.quit()
  
  How do you do this properly?
  Second question: I suppose that limiting the engine on time chess.engine.Limit(time=1) I will get different results depending on the specs of the PC where Stockfish is running. How do I make the results somehow independent of that?
  Considering to use an average pc (i5 8th generation, 16gb of ram) which depth should I chose chess.engine.Limit(depth=20) the get more or less the same results of chess.engine.Limit(time=1)?
  
  **Tags:** engines, stockfish, computer-chess, python
  
  > **Comments:**
  > **username_1**: When you analyze the position by skill level what search info you would like to get? Is it the bestmove, the evaluation, nodes, nps?  
  > **username_0**: the evaluation, precisely in Cp or Mate. But is that important?  
  > **username_1**: It is important because of your posted code. In that code you did not extract the score. I will post answer later on how you will get the score.  
  
  ---
  
  ## 1 answers
  
  ### Answer by username_1 on 2022-05-27. Score: 3 | Accepted answer
  
  Use engine.play because you are using skill level, note use info parameter to get the score. You don't need to worry about the hash table because you quit the engine for every skill levels.
  Method 1
  Code
  import chess.engine
  import logging
  
  # Uncomment to see the log in console.
  # logging.basicConfig(level=logging.DEBUG)
  
  
  stockfishpath = 'sf15.exe'
  fens = [
      'r4rk1/5pp1/5q2/p2n2p1/2R3P1/7P/P1Q2P2/2R2BK1 w - - 0 1',
      'rn1qkbnr/pp2pppp/2p5/3p1b2/3P4/4PN2/PPP2PPP/RNBQKB1R w KQkq - 0 1'
  ]
  skill_levels=[2,10,20]
  analysis_time_sec = 2
  hash_mb = 128
  
  for fen in fens:
      print(f'FEN: {fen}')
  
      for s in skill_levels:
          engine = chess.engine.SimpleEngine.popen_uci(stockfishpath)
          engine.configure({"Skill Level": s})
          engine.configure({"Hash": hash_mb})
          board = chess.Board(fen)
          result = engine.play(board, chess.engine.Limit(time=analysis_time_sec), info=chess.engine.INFO_SCORE)
          engine.quit()
  
          bestmove = board.san(result.move)
          score_cp = result.info['score'].relative.score(mate_score=32000)
          score_mate = result.info['score'].relative.mate()
  
          print(f'skill level: {s}')
          print(f'score cp: {score_cp}')
          print(f'score mate: {score_mate}')
          print(f'bestmove: {bestmove}')
          print()
  
  
  Output
  FEN: r4rk1/5pp1/5q2/p2n2p1/2R3P1/7P/P1Q2P2/2R2BK1 w - - 0 1
  skill level: 2
  score cp: 57
  score mate: None
  bestmove: Rd1
  
  skill level: 10
  score cp: 57
  score mate: None
  bestmove: Rc6
  
  skill level: 20
  score cp: 45
  score mate: None
  bestmove: Rc6
  
  FEN: rn1qkbnr/pp2pppp/2p5/3p1b2/3P4/4PN2/PPP2PPP/RNBQKB1R w KQkq - 0 1
  skill level: 2
  score cp: 34
  score mate: None
  bestmove: Nh4
  
  skill level: 10
  score cp: 34
  score mate: None
  bestmove: Bd3
  
  skill level: 20
  score cp: 23
  score mate: None
  bestmove: Bd3
  
  Regarding your second question do the following:
  
  Run the engine
  Set engine options you like, e.g
  setoption name Hash value 128
  Send the command
  go movetime 1000
  that will search the engine for 1000 ms or 1 sec.
  Sample output:
  
  info string NNUE evaluation using nn-6877cd24400e.nnue enabled
  info depth 1 seldepth 1 multipv 1 score cp 30 nodes 20 nps 10000 tbhits 0 time 2 pv e2e4
  info depth 2 seldepth 2 multipv 1 score cp 84 nodes 45 nps 22500 tbhits 0 time 2 pv e2e4 a7a6
  info depth 3 seldepth 3 multipv 1 score cp 37 nodes 191 nps 63666 tbhits 0 time 3 pv c2c4 a7a6 e2e4
  info depth 4 seldepth 4 multipv 1 score cp 158 nodes 264 nps 88000 tbhits 0 time 3 pv c2c4
  info depth 5 seldepth 6 multipv 1 score cp 49 nodes 1449 nps 241500 tbhits 0 time 6 pv e2e4 d7d5 e4d5 d8d5 d2d4
  info depth 6 seldepth 6 multipv 1 score cp 36 nodes 3954 nps 395400 tbhits 0 time 10 pv e2e4 c7c5 c2c3 b8c6
  info depth 7 seldepth 9 multipv 1 score cp 37 nodes 6269 nps 447785 tbhits 0 time 14 pv e2e4 c7c5 g1f3 b8c6 d2d4 c5d4 f3d4 g8f6
  info depth 8 seldepth 12 multipv 1 score cp 30 nodes 11049 nps 502227 tbhits 0 time 22 pv e2e4 e7e5 g1f3 b8c6 d2d4 e5d4 f3d4 g8f6 d4c6 b7c6
  info depth 9 seldepth 13 multipv 1 score cp 30 nodes 15347 nps 495064 tbhits 0 time 31 pv e2e4 e7e6 d2d4 d7d5 b1d2 g8f6 e4e5 f6d7 c2c3 c7c5 g1f3
  info depth 10 seldepth 14 multipv 1 score cp 37 nodes 23259 nps 540906 tbhits 0 time 43 pv e2e4 c7c5 g1f3 d7d6 f1b5 c8d7 b5d7 d8d7 e1g1
  info depth 11 seldepth 14 multipv 1 score cp 45 nodes 40064 nps 556444 tbhits 0 time 72 pv e2e4 c7c5 g1f3 e7e6 g2g3 d7d6 d2d4 c5d4 f3d4 g8f6
  info depth 12 seldepth 15 multipv 1 score cp 38 nodes 57767 nps 571950 tbhits 0 time 101 pv e2e4 c7c5 g1f3 b8c6 f1b5 c6d4 b5e2 e7e6 f3d4 c5d4 e1g1 a7a6
  info depth 13 seldepth 15 multipv 1 score cp 46 nodes 97551 nps 602166 tbhits 0 time 162 pv d2d4 d7d5 g1f3 g8f6 c2c4 e7e6 g2g3 f8e7 f1g2 e8g8 e1g1 d5c4 b1d2
  info depth 14 seldepth 20 multipv 1 score cp 38 nodes 152736 nps 606095 tbhits 0 time 252 pv d2d4 d7d5 g1f3 g8f6 c2c4 c7c6 b1c3 g7g6 e2e3 f8g7 f1e2 e8g8 e1g1 d5c4 e2c4
  info depth 15 seldepth 17 multipv 1 score cp 43 nodes 214622 nps 614962 tbhits 0 time 349 pv e2e4 c7c5 g1f3 d7d6 f1b5 c8d7 b5d7 b8d7 e1g1 g7g6 f1e1 g8f6 c2c3 f8g7 d2d4 c5d4
  info depth 16 seldepth 23 multipv 1 score cp 33 nodes 413957 nps 619696 tbhits 0 time 668 pv e2e4 c7c5 g1f3 d7d6 f1b5 b8d7 e1g1 a7a6 b5d7 c8d7 f1e1 e7e5 c2c3 g8e7 d2d4 e7g6 d4e5 d6e5 c3c4 f8e7 b1c3 e8g8 c3d5
  info depth 17 seldepth 24 multipv 1 score cp 35 nodes 525134 nps 621460 tbhits 0 time 845 pv e2e4 c7c5 c2c3 g8f6 e4e5 f6d5 d2d4 c5d4 c3d4 b8c6 g1f3 e7e6 b1c3 d7d6 f1d3 d6e5 d4e5 f8b4
  info depth 18 seldepth 24 multipv 1 score cp 35 nodes 619893 nps 618655 hashfull 37 tbhits 0 time 1002 pv e2e4 c7c5 c2c3 g8f6 e4e5 f6d5 d2d4 c5d4 c3d4 b8c6 g1f3 e7e6 b1c3 d7d6 f1d3 d6e5 d4e5 f8b4
  bestmove e2e4 ponder c7c5
  
  So in my PC the depth reached in single thread for 1 sec search is 18.
  Method 2
  Code
  """
  Evaluate positions with skill levels and get the correct score
  of the move corresponding to the skill level. Also Applicable
  to uci engines that does not show correct score for the move when playing suboptimal moves.
  """
  
  import chess.engine
  # import logging
  
  # Uncomment to see the log in console.
  # logging.basicConfig(level=logging.DEBUG)
  
  
  def analyze_position(stockfishpath, board, root_moves=None, skill_level=20, hash_mb=16, time_sec=1):
      """
      Analyze board position and return bestmove, score_cp and score_mate.
      """
      engine = chess.engine.SimpleEngine.popen_uci(stockfishpath)
      engine.configure({"Skill Level": skill_level})
      engine.configure({"Hash": hash_mb})
      result = engine.play(board, chess.engine.Limit(time=time_sec), info=chess.engine.INFO_SCORE, root_moves=root_moves)
      engine.quit()
  
      score_cp = result.info['score'].relative.score(mate_score=32000)
      score_mate = result.info['score'].relative.mate()
      bestmove = result.move
  
      return bestmove, score_cp, score_mate
  
  
  def main():
      stockfishpath = 'sf15.exe'
      fens = [
          '3K4/8/8/p2k4/pp1B4/N5N1/P2Q4/8 w - - 0 1'
      ]
      skill_levels=[2,10,20]
      analysis_time_sec = 1
      hash_mb = 128
  
      for fen in fens:
          print(f'FEN: {fen}')
  
          for s in skill_levels:
              board = chess.Board(fen)
              bestmove, score_cp, score_mate = analyze_position(
                  stockfishpath, board, root_moves=None, skill_level=s,
                  hash_mb=hash_mb, time_sec=analysis_time_sec)
  
              # Restart the engine to get the actual score of the bestmove returned when skill level is not best.
              # This is because the current stockfish (v15) does not report the correct score based on the bestmove it returned.
              # We will use the searchmoves  command to restrict the engine to search only this move.
              if s < 20:
                  # We will set the max skill level because we are after on the correct score of the given move.
                  max_s = 20
                  root_move = [bestmove]
                  bestmove, score_cp, score_mate = analyze_position(
                      stockfishpath, board, root_moves=root_move, skill_level=max_s,
                      hash_mb=hash_mb, time_sec=analysis_time_sec)
  
              print(f'skill level: {s}')
              print(f'score cp: {score_cp}')
              print(f'score mate: {score_mate}')
              print(f'bestmove: {board.san(bestmove)}')
              print()
  
  
  if __name__ == '__main__':
      main()
  
  Output
  FEN: 3K4/8/8/p2k4/pp1B4/N5N1/P2Q4/8 w - - 0 1
  skill level: 2
  score cp: 31996
  score mate: 4
  bestmove: Kc7
  
  skill level: 10
  score cp: 31995
  score mate: 5
  bestmove: Bb6+
  
  skill level: 20
  score cp: 31997
  score mate: 3
  bestmove: Qd1
  
  > **Comments:**
  > **username_0**: thank you very much for your reply. That's exactly what I needed. But I have a problem, probably I cannot understand the result properly. Consider this position  `'3K4/8/8/p2k4/pp1B4/N5N1/P2Q4/8 w - - 0 1'` this is what I get `FEN: 3K4/8/8/p2k4/pp1B4/N5N1/P2Q4/8 w - - 0 1 skill level: 2 score cp: 31997 score mate: 3 bestmove: Kc7 `  why at skill level 2, the socore mate is 3 but the best move is Kc7 which does not leat to a mate in 3? Sumilarly for skill level = 10  
  > **username_1**: The engine suggests a weak move but the score it sends is not based from the suggested move this is a bug in the engine. Do you also want an accurate score based on the given move when skill level is not best? I will add new code later to my answer to determine accurate score of the move sent by the engine when skill level is not best.  
  > **username_1**: Added method 2 in the answer to get the correct score of the bestmove returned when skill level is not best or 20.  
  > **username_0**: it's perfect! thank you! Where does the 'mate_score=32000' come from?  
  > **username_1**: 32k is from sf source code.  
  
  ---
  
  