Answers.Body: <p>I don't think it's possible to isolate positional understanding from tactics completely when discussing human players.</p>
  <p>We can make some assessments when comparing engines among themselves. For instance, with the introduction of deep-learning-based engines, positional understanding has improved. But we can't really find an objective measurement of &quot;positional strength&quot; that would allow us to compare engines and humans.</p>
  
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2023-04-14T06:33:28.047Z
Answers.Text: Do you mean that discussing positional strength is meaningless since it can't be objectively measured? I'm not asking for numbers, but understanding.
Answers.UserDisplayName: Anna
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2023-04-14T07:30:19.783Z
Answers.Text: @Anna not ncessarely. There is no objective measurement but we can still make assessments like "Magnus Carlsen has a better positional understanding than you and I". The problem here is that the way engines think about chess is very different from that of humans, so in this case the comparison is harder to make.
Answers.UserDisplayName: David
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2023-04-13T13:51:03.837Z
Answers.LastActivityDate: 2023-04-13T13:51:03.837Z
Answers.UserDisplayName: David
Answers.Body: <p>The main things which stockfish considers when choosing a move are:</p>
  <ul>
  <li>Material Imbalance</li>
  <li>Positional Advantage</li>
  <li>Material Advantage</li>
  <li>Strategic Advantage for Pawns</li>
  <li>Strategic Advantage for other Pieces</li>
  <li>Incoming threats</li>
  <li>Passed Pawns</li>
  <li>Space</li>
  <li>King Safety</li>
  </ul>
  <p>Nearly all of these things could be considered parts of &quot;positional understanding&quot; but to really understand how &quot;strong&quot; positional understanding is, looking for ideas and plans is necessary, stockfish does this using a optimized alphabeta pruning search, but this doesn't demonstrate any understanding of the board in front of it at all, so while stockfish with the ability to search has great ability to find and execute ideas, without that it has far less of an understanding than humans.</p>
  <p>To put it another way, humans try to come up with general plans and find good squares, while stockfish can place value on squares, it can't picture the pieces going to them without the ability to search, so its positional understanding in that case is really quite bad. I haven't tested it or seen data for it, but i would wager it is far below club players without search depth.</p>
  
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2023-04-13T18:53:04.217Z
Answers.Text: Could you please explain what you mean by modern stockfish not using a neural net? I'm referring to the NNUE.
Answers.UserDisplayName: Anna
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2023-04-13T18:56:57.847Z
Answers.Text: my apologies, i misread an article, it does use a neural net, but not ordinary machine learning techniques. i'll correct it now
Answers.UserDisplayName: brekker
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2023-04-14T06:38:30.227Z
Answers.Text: Thanks for spending time on the answer. It seems though that "its positional understanding in that case is really quite bad" is mostly speculation? I'm also confused by your list, that seems to relate to pre-NNUE stockfish?
Answers.UserDisplayName: Anna
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2023-04-13T16:57:21.233Z
Answers.LastActivityDate: 2023-04-13T18:57:12.850Z
Answers.LastEditDate: 2023-04-13T18:57:12.850Z
Answers.UserDisplayName: brekker
Answers.Body: <p>The idea that positional understanding and tactics are different is wrong. Even a beginner like myself needs both to play reasonably well. Without tactics I will never beat any decent player. Of course, being that this is Stockfish, it does a bit better  than I would. Also, here are some in important things to consider in chess. They could be said to be either:</p>
  <ol>
  <li><strong>Threats</strong>:You can make the arguement for both. Threats could be positional understanding. After all, they are something in your position that you need to understand (to avoid a blunder).</li>
  <li><strong>King safety</strong>:Again, you can make the arguement for both. Understanding my position, I can realize that I must move my bishop to avoid mate in 1. Or I could say that I use tactics and realize that in order to win, I can't be checkmated, and reach the same conclusion</li>
  <li><strong>A strategic advantage of any kind</strong>:The same as above. I can promote a pawn soon. I can checkmate soon. I can win a rook for a knight. Etc. I understand my position that I can promote a pawn. Or promoting that pawn is part of my tactics.</li>
  <li><strong>Etc</strong>:Literally anything can be considered both. After all, everything a chess engine does is trying to win something. That is tactics. Also, there is no move that ignores the position, as that would be idiotic.</li>
  </ol>
  <p>So, to say how stong positional understanding is without tactics (or vice versa) is moot. Also consider the following opposite question. How strong is tactics without positional understanding? The answer. Nonexistent. How can one have tactics if you have no understanding of your position? You can't. How can one have positional understanding without any tactics? You can't.</p>
  <p>One other thing to note is that when the NNUE (Efficiently Updatable Neural Network) was added to Stockfish 12 it is increased the rating only by 100. While a bunch, it is not that large. This does indicate that Alpha Beta search was probably able to have positional understanding (because they are the same thing).</p>
  
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2023-04-16T10:06:50.923Z
Answers.Text: There's plenty of chess literature that distinguishes positional understanding from tactics, so unless I'm misunderstanding you this argument seem a bit reductive?
Answers.UserDisplayName: Anna
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2023-04-16T10:07:27.873Z
Answers.Text: Really then name one concrete thing that is one and not the other? @Anna
Answers.UserDisplayName: Starship
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2023-04-16T09:55:27.540Z
Answers.LastActivityDate: 2023-04-16T09:55:27.540Z
Answers.UserDisplayName: Starship
Answers.Body: <h1>It depends</h1>
  <p>Based on this <a href="https://blogs.cornell.edu/info2040/2022/09/30/game-theory-how-stockfish-mastered-chess/" rel="nofollow noreferrer">blog</a>:</p>
  <blockquote>
  <p>Stockfish uses a static evaluation function, or a function which calculates an estimate evaluation of the position using a heuristic (a manually constructed algorithm designed to fit any arbitrary chess position) which has been hand-crafted by computer scientists and chess grandmasters for over a decade. Since the evaluation function is very powerful we can assume it will do a good job most of the time to evaluate a position. However, this is not enough for a game as complex as chess – it is a Dynamic Game as opposed to a simultaneous-move game and involves niche game tree paths which can never be fully encapsulated in the problem space of an evaluation heuristic. There is a high probability that the function will evaluate a position as winning and miss a line or path of moves by the opponent which causes us to actually lose. It can also evaluate a position as losing and miss a line which is actually winning. The static evaluation function capitalizes on the deterministic nature of the game, however we need to do more to deal with the element of randomness. Hence, it is necessary for the engine to be able to calculate into the future.</p>
  </blockquote>
  <p>It states that depending on the position of the game, Stockfish when only using its static evaluation function can miss a far better outcome than what it plays and misevaluate the position. Therefore without the minimal and Alpha beta search methods, stockfish isn't considered very good when it comes to positional understanding, since it can misevaulate.</p>
  
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2023-04-17T14:34:07.620Z
Answers.Text: Thanks for your answer. If I'm not misunderstanding, Stockfish does not use the static evaluation function anymore, but instead a neural network (the NNUE)? It is the positional understanding of this network that I'm wondering about.
Answers.UserDisplayName: Anna
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2023-04-17T15:57:53.833Z
Answers.Text: @Anna is right, Stockfish uses a NNUE since stockfish 12
Answers.UserDisplayName: Starship
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2023-04-17T13:06:45.813Z
Answers.LastActivityDate: 2023-04-17T13:06:45.813Z
Answers.UserDisplayName: DialFrost
Answers.Body: <p>Without searching we can get the nnue score for each legal move from a given position.</p>
  <p>Stockfish has an <code>eval</code> command to return the static evaluation of the position. It can be classical, nnue and hybrid.</p>
  <p>Run stockfish and enter the following as an example.</p>
  <pre><code>position fen 5rk1/1pq2ppb/2p1p2p/4P3/3R4/2Q3P1/1P2PPBP/6K1 w - - 0 1
  eval
  </code></pre>
  <p><strong>Output</strong></p>
  <pre><code>Classical evaluation   +0.22 (white side)
  NNUE evaluation        +0.45 (white side)
  Final evaluation       +0.50 (white side) [with scaled NNUE, hybrid, ...]
  </code></pre>
  <p>We are interested on<br />
  <code>NNUE evaluation        +0.45 (white side)</code></p>
  <p>To get the nnue score of the legal moves without searching, we need the position and the legal moves.</p>
  <ol>
  <li>Setup the position + legal move to stockfish</li>
  <li>Send eval the command to get nnue score.</li>
  <li>Negate the score as the move is pushed on the board.</li>
  <li>Convert wpov (White Point of View) score to spov (Side Point of View) score.</li>
  </ol>
  <pre><code>position fen 5rk1/1pq2ppb/2p1p2p/4P3/3R4/2Q3P1/1P2PPBP/6K1 w - - 0 1 moves d4d8
  eval
  </code></pre>
  <p>With that we will get the nnue score of the move d4d8.</p>
  <p><strong>Output</strong></p>
  <pre><code>NNUE evaluation        +0.12 (white side)
  </code></pre>
  <pre><code>eval = 0.12
  evalcp = 0.12 * 100 = 12
  </code></pre>
  <p>The side to move after the move is black.</p>
  <pre><code>evalcp = -1 * evalcp = -12
  </code></pre>
  <p>The move is pushed so we negate the eval again.</p>
  <pre><code>evalcp = -1 * evalcp = -1 * (-12) = 12
  </code></pre>
  <p>So the nnue eval of the move d4d8 from the position <code>5rk1/1pq2ppb/2p1p2p/4P3/3R4/2Q3P1/1P2PPBP/6K1 w - - 0 1</code> is 12 cp (spov).</p>
  <p>Do the same for other legal moves to get the eval.</p>
  <p>Sort the move eval in descending order to get the top move and eval from the given position. Remember the top move, as we will get the score of this move from the stockfish full strength multipv analysis.</p>
  <p><strong>Error Calculation</strong></p>
  <p>To get the error, we run stockfish say 10 sec at full strength at multipv value equal to the legal moves of the positions.</p>
  <p>The top 1 score less the score of the top move from nnue eval is the error. Take the absolute difference to get a positive error value.</p>
  <p><strong>Where to get the test positions?</strong></p>
  <p>You can take any game in pgn format. I use tata steel 2023 games. These games are played by some top human players.</p>
  <p>Parse each position in the game, record the game move, get the score of the move and calculate the error based from top 1 move score from stockfish. The score of game move can be found from the stockfish multipv analysis.</p>
  <p><strong>code</strong></p>
  <p>The full code below will save its analysis in csv file for later processing.</p>
  <p>main.py</p>
  <pre><code>&quot;&quot;&quot;Estimate the strength of nnue eval.
  
  requirements:
    chess
    pandas
  
  installation:
    pip install chess
    pip install pandas
  &quot;&quot;&quot;
  
  
  import subprocess
  
  import chess
  import chess.pgn
  import chess.engine
  import pandas as pd
  
  pd.set_option('display.width', 1000)
  pd.set_option('display.expand_frame_repr', False)
  pd.set_option('max_colwidth', 800)
  
  
  def nnue_eval(enginefn, epd):
      &quot;&quot;&quot;Find the best move with highest eval.
  
      Run engine, setup position and send eval command.   
      Save eval and legal moves from the given epd. 
      &quot;&quot;&quot;
      board = chess.Board(epd)
      epds, moves, scores = [], [], []
  
      engine = subprocess.Popen(enginefn, stdin=subprocess.PIPE,
                                stdout=subprocess.PIPE,
                                stderr=subprocess.STDOUT,
                                universal_newlines=True, bufsize=1,
                                creationflags=subprocess.CREATE_NO_WINDOW)
      
      engine.stdin.write('uci\n')
      for eline in iter(engine.stdout.readline, ''):
          line = eline.strip()
          if 'uciok' in line:
              break
  
      engine.stdin.write('isready\n')
      for eline in iter(engine.stdout.readline, ''):
          line = eline.strip()
          if 'readyok' in line:
              break
  
      # Visit all moves and get the eval.    
      for move in board.legal_moves:
          b = board.copy()
          san_move = b.san(move)
          b.push(move)
  
          # If side to move is in check, don't evaluate.
          if b.is_check():
              continue
  
          fen = b.fen()
          engine.stdin.write(f'position fen {fen}\n')
          engine.stdin.write('eval\n')
  
          pawn_value_wpov = None
          for eline in iter(engine.stdout.readline, ''):
              line = eline.strip()
  
              # NNUE evaluation        +0.19 (white side)
              if 'NNUE evaluation' in line and not 'info string' in line:
                  value = line.split('NNUE evaluation')[1]
                  value = value.split('(white side)')[0].strip()
                  pawn_value_wpov = float(value)
  
                  epds.append(epd)
                  moves.append(san_move)
  
                  score = round(100*pawn_value_wpov)
  
                  # Convert to spov.
                  if b.turn == chess.BLACK:
                      score = -score
                  score = -score  # move was pushed
                       
                  scores.append(score)
                  break
              
      engine.stdin.write('quit\n')
              
      df = pd.DataFrame({'epds': epds, 'moves': moves, 'scores': scores})
  
      df = df.sort_values(by=['scores'], ascending=[False])
  
      return  df
  
  
  def main():
      max_games = 5
      movetimesec = 10
      pgnfn = r'F:\project\pgn\tatamast23.pgn'
      enginefn = r'F:\Chess\Engines\stockfish\stockfish_15.1_win_x64_popcnt\stockfish-windows-2022-x86-64-modern.exe'
      gcnt = 0
  
      engine = chess.engine.SimpleEngine.popen_uci(enginefn)
  
      alldf = []
  
      with open(pgnfn) as pgn:
          while True:
              game = chess.pgn.read_game(pgn)
              if game is None:
                  break
  
              gcnt += 1
              print(f'game {gcnt}')
  
              wp = game.headers['White']
              bp = game.headers['Black']
  
              data = []
  
              for node in game.mainline():
                  board = node.parent.board()
                  gmove = node.move
                  gmove_san = board.san(gmove)
                  gply = board.ply()
                  epd = board.epd()
                  fmvn = board.fullmove_number
  
                  gmove_is_check = True if '+' in gmove_san else False
  
                  if board.is_check():
                      continue
  
                  # Don't analyze moves below 20.
                  if gply &lt; 40:
                      continue
  
                  legal_moves = board.legal_moves.count()
  
                  # Analyze this board with the engine. Get all the legal moves evaluation.
                  info = engine.analyse(board, chess.engine.Limit(time=movetimesec), multipv=legal_moves)
                  epds, scores, moves = [], [], []
                  top_move_is_tactical = False
                  top1_move = None
                  for i in range(min(legal_moves, board.legal_moves.count())):
                      score = info[i]['score'].relative.score(mate_score=32000)
                      pv = info[i]['pv']
                      move = pv[0]
                      san_move = board.san(move)
  
                      if i == 0:
                          top1_move = san_move
  
                      # Exclude tactical moves.
                      if i &lt;= 5 and ('+' in san_move or 'x' in san_move or '=' in san_move):
                          top_move_is_tactical = True
                          break
  
                      epds.append(epd)
                      scores.append(score)
                      moves.append(san_move)
  
                  if not top_move_is_tactical and not gmove_is_check:
                      top_df = pd.DataFrame({'epds': epds, 'moves': moves, 'scores': scores})
                      print(f'top_df:\n{top_df}\n')
  
                      nnue_df = nnue_eval(enginefn, epd)                
                      nnue_df = nnue_df.reset_index(drop=True)
                      print(f'nnue_df:\n{nnue_df}\n')
  
                      print(f'game move:\n{gmove_san}\n')
  
                      # Get top nnue score based from top_df data.
                      top1_score = top_df['scores'].iloc[0]
                      top_nnue_move = nnue_df['moves'].iloc[0]
                      top_nnue_move_score_from_top_df = top_df.loc[top_df.moves == top_nnue_move]['scores'].iloc[0]
  
                      # Get actual score of game move based from top_df data.
                      gmove_score_from_top_df = top_df.loc[top_df.moves == gmove_san]['scores'].iloc[0]
  
                      print(board)
                      print()
                      print(epd)
                      print(f'top_1_score: {top1_score}, top_nnue_score: {top_nnue_move_score_from_top_df}, gmove_score: {gmove_score_from_top_df}')
                      print()
  
                      if board.turn == chess.WHITE:
                          data.append([gcnt, epd, fmvn, wp, gmove_san, gmove_score_from_top_df, top1_move, top1_score, top_nnue_move, top_nnue_move_score_from_top_df])
                      else:
                          data.append([gcnt, epd, fmvn, bp, gmove_san, gmove_score_from_top_df, top1_move, top1_score, top_nnue_move, top_nnue_move_score_from_top_df])
  
                      if abs(top1_score) &gt; 500 and abs(gmove_score_from_top_df) &gt; 500:
                          break
  
              # Save to dataframe.
              df = pd.DataFrame(data, columns=['GameNum', 'Epd', 'MoveNum', 'Name', 'GameMove', 'GameMoveScore', 'EngineMove', 'EngineScore', 'NNUEMove', 'NNUEScore'])
              alldf.append(df)
  
              if gcnt &gt;= max_games:
                  break
  
      engine.quit()
  
      # Save data to csv file. Print average error in cp.
      df = pd.concat(alldf, ignore_index=True)
      df.to_csv('data.csv', index=False)
      print(df)
  
      df_error = df.copy()
  
      df_error['HumanError'] = abs(df_error['EngineScore'] - df_error['GameMoveScore'])
      df_error['NNUEError'] = abs(df_error['EngineScore'] - df_error['NNUEScore'])
      print(df_error)
  
      average_human_error = df_error['HumanError'].mean().round()
      average_nnue_error = df_error['NNUEError'].mean().round()
  
      print(f'AveHumanError: {average_human_error}')
      print(f'AveNNUEError: {average_nnue_error}')
  
  
  if __name__ == '__main__':
      main()
  </code></pre>
  <p>Modify the stockfish and pgn paths to suit your needs. See,</p>
  <pre><code>pgnfn = r'F:\project\pgn\tatamast23.pgn'
  enginefn = r'F:\Chess\Engines\stockfish\stockfish_15.1_win_x64_popcnt\stockfish-windows-2022-x86-64-modern.exe'
  </code></pre>
  <p><strong>command line</strong></p>
  <pre><code>python main.py &gt;log.txt
  </code></pre>
  <p><strong>output</strong></p>
  <pre><code>    GameNum                                                Epd  MoveNum                    Name GameMove  GameMoveScore EngineMove  EngineScore NNUEMove  NNUEScore  HumanError  NNUEError
  0         1  5rk1/1p1q1ppb/2p1p2p/4P3/R7/2Q3P1/1P2PPBP/6K1 ...       23         Carlsen, Magnus      Rd4             52        Rd4           52      Rd4         52           0          0
  1         1  5rk1/1pq2ppb/2p1p2p/4P3/3R4/2Q3P1/1P2PPBP/6K1 ...       24         Carlsen, Magnus      Rd6             62        Rd6           62      Rd7       -548           0        610
  2         1  5rk1/1pq2ppb/2pRp2p/4P3/8/2Q3P1/1P2PPBP/6K1 b - -       24          Aronian, Levon      Ra8            -73        Qb6          -51     Qxd6       -520          22        469
  3         1  r5k1/1pq2ppb/2pRp2p/4P3/8/2Q3P1/1P2PPBP/6K1 w - -       25         Carlsen, Magnus      Bf3             59         h4           69     Rxc6       -466          10        535
  4         1  r5k1/1pq2ppb/2pRp2p/4P3/8/2Q2BP1/1P2PP1P/6K1 b...       25          Aronian, Levon      Bg6            -38        Bg6          -38     Qxd6       -504           0        466
  ..      ...                                                ...      ...                     ...      ...            ...        ...          ...      ...        ...         ...        ...
  71        5                 8/p7/1p5R/3P1kp1/8/1K5P/r7/8 b - -       38  Abdusattorov, Nodirbek      Ra1             67        Rd2           67      Rd2         67           0          0
  72        5                 8/p7/1p5R/3P1kp1/8/1K5P/8/r7 w - -       39        Rapport, Richard      Kc4           -124        Kc2          -59      Kc4       -124          65         65
  73        5                  8/p7/7R/1pKP1kp1/8/7P/8/3r4 b - -       42  Abdusattorov, Nodirbek       b4            205         b4          205       a5         35           0        170
  74        5                 7R/p7/8/2KP1kp1/1p6/7P/8/3r4 b - -       43  Abdusattorov, Nodirbek       b3            205         b3          205       a5        103           0        102
  75        5               4R3/p7/8/2KP2p1/8/1p1k3P/8/3r4 w - -       46        Rapport, Richard       d6           -686        Rb8         -641       d6       -686          45         45
  
  [76 rows x 12 columns]
  AveHumanError: 8.0
  AveNNUEError: 251.0
  </code></pre>
  <p>The function <code>nnue_eval()</code> will calculate the nnue eval of the legal moves from the given position or epd.</p>
  <p>I place the code in <a href="https://github.com/fsmosca/nnue-strength" rel="nofollow noreferrer">github</a> repository to easier maintain it.</p>
  
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2023-04-20T13:23:55.243Z
Answers.LastActivityDate: 2023-04-21T01:45:31.423Z
Answers.LastEditDate: 2023-04-21T01:45:31.423Z
Answers.UserDisplayName: ferdy
Body: <p>At zero depth/search, i.e. only using its neural net, Stockfish's tactics  would, of course, be abysmal, but how strong is its positional evaluation? Can it be compared to grandmasters or to club players?</p>
  <p>Note: Stockfish NNUE uses a mixture of AB search and neural network. This question is asking how strong the neural network positional evaluation part of Stockfish's evaluation is.</p>
  
Comments.ContentLicense: CC BY-SA 4.0
Comments.CreationDate: 2023-04-17T10:17:26.187Z
Comments.Text: At zero depth/search, stockfish will not be able to produce a move. What it can do is output the static evaluation. Can you measure the strength of an engine without knowing  its recommended bestmove from a given position?
Comments.UserDisplayName: ferdy
Comments.ContentLicense: CC BY-SA 4.0
Comments.CreationDate: 2023-04-17T12:37:43.490Z
Comments.Text: @ferdy Thanks for pointing out my mistake. I must have meant depth 1, right? Where Stockfish uses its neural net to evaluate every possible move, but does not consider any responses to them.
Comments.UserDisplayName: Anna
Comments.ContentLicense: CC BY-SA 4.0
Comments.CreationDate: 2023-04-18T01:24:49.767Z
Comments.Text: Like many engines, stockfish has two main searches the main search and the quiescence search. Search depth 1 refers to the main search. After main search depth 1, the program will use quiescence search where it will evaluate tactical moves such as capture, checking, check evasion and promote moves. So even at search depth 1, the engine is capable of resolving tactical moves behind the scene. We can revise stockfish and disable its quiescence search if that make sense of what your are trying to achieve.
Comments.UserDisplayName: ferdy
Comments.ContentLicense: CC BY-SA 4.0
Comments.CreationDate: 2023-04-18T07:55:13.383Z
Comments.Text: Thank you @ferdy, I had no idea about quiescence search. I thought this could be answered by playing a couple games vs Stockfish at depth 1, but apparently it's more complicated!
Comments.UserDisplayName: Anna
Comments.ContentLicense: CC BY-SA 4.0
Comments.CreationDate: 2023-04-18T11:37:31.093Z
Comments.Text: This is the reason why even at depth 1 the engine can see tactics because of the presence of this secondary search. The evaluation function (NNUE, hybrid, classical) is mostly applied on quiet positions at quiescence search. It is not very strong as it is limited to depth 1, but can still challenge some titled players. Also since you are interested on NNUE only, stockfish has to be revised to only use NNUE. I think the default is hybrid.
Comments.UserDisplayName: ferdy
Comments.ContentLicense: CC BY-SA 4.0
Comments.CreationDate: 2023-04-19T10:34:00.273Z
Comments.Text: Without AB search, we cannot use a search even at depth 1. So this is purely an evaluation. We may not know its best move from a given position, but we can get its evaluation. You can try this approach. 1) Find positions from actual game to get the test positions, 2) test stockfish with this position and record its score with the use of eval command. You can get pure NNUE evaluation. 3) Run full strength stockfish say at 30 seconds, and save its bestmove and bestscore. 4) Now get the evaluation error. `eval_error = abs(full_stockfish_eval - eval_commad_stockfish_eval)`
Comments.UserDisplayName: ferdy
Comments.ContentLicense: CC BY-SA 4.0
Comments.CreationDate: 2023-04-19T10:37:29.163Z
Comments.Text: Thanks for your expertise @ferdy! When I find the time, I think I'll see if I can manage to simply check the pure NNUE evaluation of each legal move from some position.
Comments.UserDisplayName: Anna
Comments.ContentLicense: CC BY-SA 4.0
Comments.CreationDate: 2023-04-19T10:37:53.327Z
Comments.Text: (by evaluation of a move I mean the evaluation of the position resulting from the move, of course)
Comments.UserDisplayName: Anna
Comments.ContentLicense: CC BY-SA 4.0
Comments.CreationDate: 2023-04-19T10:39:09.647Z
Comments.Text: High error indicates a weaker engine. Since that position is from a game, get the actual move played by a player, use stockfish to measure the score of the move played by that player. `player_move_error = abs(full_stockfish_eval - player_move_eval)`. If eval_error is greater than player_error, then player is stronger than eval_command_stockfish based on that specific test position.
Comments.UserDisplayName: ferdy
Comments.ContentLicense: CC BY-SA 4.0
Comments.CreationDate: 2023-04-19T10:48:19.790Z
Comments.Text: "When I find the time, I think I'll see if I can manage to simply check the pure NNUE evaluation of each legal move from some position. – 
  Anna" Yes you can do that.
Comments.UserDisplayName: ferdy
Comments.ContentLicense: CC BY-SA 4.0
Comments.CreationDate: 2023-04-20T11:15:04.377Z
Comments.Text: I tried to calculate the error from 5 tatasteel2023 games. The pure NNUE evaluation is worst compared to human. `AveHumanError: 8 cp` and `AveNNUEError: 251 cp`. Without AB search, NNUE cannot compete at this level. The test positions are selected so that tactical positions are minimized. There should not be captures, checking and promote moves from the top 6 moves from the full strength stockfish multipv analysis. Also I am able to get the pure nnue evaluation for each legal move from the given position.
Comments.UserDisplayName: ferdy
Comments.ContentLicense: CC BY-SA 4.0
Comments.CreationDate: 2023-04-20T11:35:00.990Z
Comments.Text: Thank you @ferdy, very interesting! Could you share your patch for grabbing the pure NNUE evaluation? Also, if you could just post an answer with all of your comments I'd be happy to award it the bounty, your comments have been very helpful for my understanding.
Comments.UserDisplayName: Anna
Comments.ContentLicense: CC BY-SA 4.0
Comments.CreationDate: 2023-04-20T13:29:39.837Z
Comments.Text: Just posted my answer with python code.
Comments.UserDisplayName: ferdy
Tags: <stockfish><positional-play><computer-chess>
Title: How strong is Stockfish's positional understanding without AB search?
ThreadText: # How strong is Stockfish's positional understanding without AB search?
  
  (Asked by: username_0 on 2023-04-13)
  
  At zero depth/search, i.e. only using its neural net, Stockfish's tactics would, of course, be abysmal, but how strong is its positional evaluation? Can it be compared to grandmasters or to club players?
  
  Note: Stockfish NNUE uses a mixture of AB search and neural network. This question is asking how strong the neural network positional evaluation part of Stockfish's evaluation is.
  
  **Tags:** stockfish, positional-play, computer-chess
  
  > **Comments:**
  > **username_1**: At zero depth/search, stockfish will not be able to produce a move. What it can do is output the static evaluation. Can you measure the strength of an engine without knowing  its recommended bestmove from a given position?  
  > **username_0**: @username_1 Thanks for pointing out my mistake. I must have meant depth 1, right? Where Stockfish uses its neural net to evaluate every possible move, but does not consider any responses to them.  
  > **username_1**: Like many engines, stockfish has two main searches the main search and the quiescence search. Search depth 1 refers to the main search. After main search depth 1, the program will use quiescence search where it will evaluate tactical moves such as capture, checking, check evasion and promote moves. So even at search depth 1, the engine is capable of resolving tactical moves behind the scene. We can revise stockfish and disable its quiescence search if that make sense of what your are trying to achieve.  
  > **username_0**: Thank you @username_1, I had no idea about quiescence search. I thought this could be answered by playing a couple games vs Stockfish at depth 1, but apparently it's more complicated!  
  > **username_1**: This is the reason why even at depth 1 the engine can see tactics because of the presence of this secondary search. The evaluation function (NNUE, hybrid, classical) is mostly applied on quiet positions at quiescence search. It is not very strong as it is limited to depth 1, but can still challenge some titled players. Also since you are interested on NNUE only, stockfish has to be revised to only use NNUE. I think the default is hybrid.  
  > **username_1**: Without AB search, we cannot use a search even at depth 1. So this is purely an evaluation. We may not know its best move from a given position, but we can get its evaluation. You can try this approach. 1) Find positions from actual game to get the test positions, 2) test stockfish with this position and record its score with the use of eval command. You can get pure NNUE evaluation. 3) Run full strength stockfish say at 30 seconds, and save its bestmove and bestscore. 4) Now get the evaluation error. `eval_error = abs(full_stockfish_eval - eval_commad_stockfish_eval)`  
  > **username_0**: Thanks for your expertise @username_1! When I find the time, I think I'll see if I can manage to simply check the pure NNUE evaluation of each legal move from some position.  
  > **username_0**: (by evaluation of a move I mean the evaluation of the position resulting from the move, of course)  
  > **username_1**: High error indicates a weaker engine. Since that position is from a game, get the actual move played by a player, use stockfish to measure the score of the move played by that player. `player_move_error = abs(full_stockfish_eval - player_move_eval)`. If eval_error is greater than player_error, then player is stronger than eval_command_stockfish based on that specific test position.  
  > **username_1**: "When I find the time, I think I'll see if I can manage to simply check the pure NNUE evaluation of each legal move from some position. –  username_0" Yes you can do that.  
  > **username_1**: I tried to calculate the error from 5 tatasteel2023 games. The pure NNUE evaluation is worst compared to human. `AveHumanError: 8 cp` and `AveNNUEError: 251 cp`. Without AB search, NNUE cannot compete at this level. The test positions are selected so that tactical positions are minimized. There should not be captures, checking and promote moves from the top 6 moves from the full strength stockfish multipv analysis. Also I am able to get the pure nnue evaluation for each legal move from the given position.  
  > **username_0**: Thank you @username_1, very interesting! Could you share your patch for grabbing the pure NNUE evaluation? Also, if you could just post an answer with all of your comments I'd be happy to award it the bounty, your comments have been very helpful for my understanding.  
  > **username_1**: Just posted my answer with python code.  
  
  ---
  
  ## 5 answers
  
  ### Answer by username_1 on 2023-04-20. Score: 6 | Accepted answer
  
  Without searching we can get the nnue score for each legal move from a given position.
  
  Stockfish has an `eval` command to return the static evaluation of the position. It can be classical, nnue and hybrid.
  
  Run stockfish and enter the following as an example.
  
  ```
  position fen 5rk1/1pq2ppb/2p1p2p/4P3/3R4/2Q3P1/1P2PPBP/6K1 w - - 0 1
  eval
  
  ```
  
  **Output**
  
  ```
  Classical evaluation   +0.22 (white side)
  NNUE evaluation        +0.45 (white side)
  Final evaluation       +0.50 (white side) [with scaled NNUE, hybrid, ...]
  
  ```
  
  We are interested on  
  `NNUE evaluation +0.45 (white side)`
  
  To get the nnue score of the legal moves without searching, we need the position and the legal moves.
  
  1. Setup the position + legal move to stockfish
  2. Send eval the command to get nnue score.
  3. Negate the score as the move is pushed on the board.
  4. Convert wpov (White Point of View) score to spov (Side Point of View) score.
  
  ```
  position fen 5rk1/1pq2ppb/2p1p2p/4P3/3R4/2Q3P1/1P2PPBP/6K1 w - - 0 1 moves d4d8
  eval
  
  ```
  
  With that we will get the nnue score of the move d4d8.
  
  **Output**
  
  ```
  NNUE evaluation        +0.12 (white side)
  
  ```
  
  ```
  eval = 0.12
  evalcp = 0.12 * 100 = 12
  
  ```
  
  The side to move after the move is black.
  
  ```
  evalcp = -1 * evalcp = -12
  
  ```
  
  The move is pushed so we negate the eval again.
  
  ```
  evalcp = -1 * evalcp = -1 * (-12) = 12
  
  ```
  
  So the nnue eval of the move d4d8 from the position `5rk1/1pq2ppb/2p1p2p/4P3/3R4/2Q3P1/1P2PPBP/6K1 w - - 0 1` is 12 cp (spov).
  
  Do the same for other legal moves to get the eval.
  
  Sort the move eval in descending order to get the top move and eval from the given position. Remember the top move, as we will get the score of this move from the stockfish full strength multipv analysis.
  
  **Error Calculation**
  
  To get the error, we run stockfish say 10 sec at full strength at multipv value equal to the legal moves of the positions.
  
  The top 1 score less the score of the top move from nnue eval is the error. Take the absolute difference to get a positive error value.
  
  **Where to get the test positions?**
  
  You can take any game in pgn format. I use tata steel 2023 games. These games are played by some top human players.
  
  Parse each position in the game, record the game move, get the score of the move and calculate the error based from top 1 move score from stockfish. The score of game move can be found from the stockfish multipv analysis.
  
  **code**
  
  The full code below will save its analysis in csv file for later processing.
  
  main.py
  
  ```
  """Estimate the strength of nnue eval.
  
  requirements:
    chess
    pandas
  
  installation:
    pip install chess
    pip install pandas
  """
  
  import subprocess
  
  import chess
  import chess.pgn
  import chess.engine
  import pandas as pd
  
  pd.set_option('display.width', 1000)
  pd.set_option('display.expand_frame_repr', False)
  pd.set_option('max_colwidth', 800)
  
  def nnue_eval(enginefn, epd):
      """Find the best move with highest eval.
  
      Run engine, setup position and send eval command.   
      Save eval and legal moves from the given epd. 
      """
      board = chess.Board(epd)
      epds, moves, scores = [], [], []
  
      engine = subprocess.Popen(enginefn, stdin=subprocess.PIPE,
                                stdout=subprocess.PIPE,
                                stderr=subprocess.STDOUT,
                                universal_newlines=True, bufsize=1,
                                creationflags=subprocess.CREATE_NO_WINDOW)
      
      engine.stdin.write('uci\n')
      for eline in iter(engine.stdout.readline, ''):
          line = eline.strip()
          if 'uciok' in line:
              break
  
      engine.stdin.write('isready\n')
      for eline in iter(engine.stdout.readline, ''):
          line = eline.strip()
          if 'readyok' in line:
              break
  
      # Visit all moves and get the eval.    
      for move in board.legal_moves:
          b = board.copy()
          san_move = b.san(move)
          b.push(move)
  
          # If side to move is in check, don't evaluate.
          if b.is_check():
              continue
  
          fen = b.fen()
          engine.stdin.write(f'position fen {fen}\n')
          engine.stdin.write('eval\n')
  
          pawn_value_wpov = None
          for eline in iter(engine.stdout.readline, ''):
              line = eline.strip()
  
              # NNUE evaluation        +0.19 (white side)
              if 'NNUE evaluation' in line and not 'info string' in line:
                  value = line.split('NNUE evaluation')[1]
                  value = value.split('(white side)')[0].strip()
                  pawn_value_wpov = float(value)
  
                  epds.append(epd)
                  moves.append(san_move)
  
                  score = round(100*pawn_value_wpov)
  
                  # Convert to spov.
                  if b.turn == chess.BLACK:
                      score = -score
                  score = -score  # move was pushed
                       
                  scores.append(score)
                  break
              
      engine.stdin.write('quit\n')
              
      df = pd.DataFrame({'epds': epds, 'moves': moves, 'scores': scores})
  
      df = df.sort_values(by=['scores'], ascending=[False])
  
      return  df
  
  def main():
      max_games = 5
      movetimesec = 10
      pgnfn = r'F:\project\pgn\tatamast23.pgn'
      enginefn = r'F:\Chess\Engines\stockfish\stockfish_15.1_win_x64_popcnt\stockfish-windows-2022-x86-64-modern.exe'
      gcnt = 0
  
      engine = chess.engine.SimpleEngine.popen_uci(enginefn)
  
      alldf = []
  
      with open(pgnfn) as pgn:
          while True:
              game = chess.pgn.read_game(pgn)
              if game is None:
                  break
  
              gcnt += 1
              print(f'game {gcnt}')
  
              wp = game.headers['White']
              bp = game.headers['Black']
  
              data = []
  
              for node in game.mainline():
                  board = node.parent.board()
                  gmove = node.move
                  gmove_san = board.san(gmove)
                  gply = board.ply()
                  epd = board.epd()
                  fmvn = board.fullmove_number
  
                  gmove_is_check = True if '+' in gmove_san else False
  
                  if board.is_check():
                      continue
  
                  # Don't analyze moves below 20.
                  if gply < 40:
                      continue
  
                  legal_moves = board.legal_moves.count()
  
                  # Analyze this board with the engine. Get all the legal moves evaluation.
                  info = engine.analyse(board, chess.engine.Limit(time=movetimesec), multipv=legal_moves)
                  epds, scores, moves = [], [], []
                  top_move_is_tactical = False
                  top1_move = None
                  for i in range(min(legal_moves, board.legal_moves.count())):
                      score = info[i]['score'].relative.score(mate_score=32000)
                      pv = info[i]['pv']
                      move = pv[0]
                      san_move = board.san(move)
  
                      if i == 0:
                          top1_move = san_move
  
                      # Exclude tactical moves.
                      if i <= 5 and ('+' in san_move or 'x' in san_move or '=' in san_move):
                          top_move_is_tactical = True
                          break
  
                      epds.append(epd)
                      scores.append(score)
                      moves.append(san_move)
  
                  if not top_move_is_tactical and not gmove_is_check:
                      top_df = pd.DataFrame({'epds': epds, 'moves': moves, 'scores': scores})
                      print(f'top_df:\n{top_df}\n')
  
                      nnue_df = nnue_eval(enginefn, epd)                
                      nnue_df = nnue_df.reset_index(drop=True)
                      print(f'nnue_df:\n{nnue_df}\n')
  
                      print(f'game move:\n{gmove_san}\n')
  
                      # Get top nnue score based from top_df data.
                      top1_score = top_df['scores'].iloc[0]
                      top_nnue_move = nnue_df['moves'].iloc[0]
                      top_nnue_move_score_from_top_df = top_df.loc[top_df.moves == top_nnue_move]['scores'].iloc[0]
  
                      # Get actual score of game move based from top_df data.
                      gmove_score_from_top_df = top_df.loc[top_df.moves == gmove_san]['scores'].iloc[0]
  
                      print(board)
                      print()
                      print(epd)
                      print(f'top_1_score: {top1_score}, top_nnue_score: {top_nnue_move_score_from_top_df}, gmove_score: {gmove_score_from_top_df}')
                      print()
  
                      if board.turn == chess.WHITE:
                          data.append([gcnt, epd, fmvn, wp, gmove_san, gmove_score_from_top_df, top1_move, top1_score, top_nnue_move, top_nnue_move_score_from_top_df])
                      else:
                          data.append([gcnt, epd, fmvn, bp, gmove_san, gmove_score_from_top_df, top1_move, top1_score, top_nnue_move, top_nnue_move_score_from_top_df])
  
                      if abs(top1_score) > 500 and abs(gmove_score_from_top_df) > 500:
                          break
  
              # Save to dataframe.
              df = pd.DataFrame(data, columns=['GameNum', 'Epd', 'MoveNum', 'Name', 'GameMove', 'GameMoveScore', 'EngineMove', 'EngineScore', 'NNUEMove', 'NNUEScore'])
              alldf.append(df)
  
              if gcnt >= max_games:
                  break
  
      engine.quit()
  
      # Save data to csv file. Print average error in cp.
      df = pd.concat(alldf, ignore_index=True)
      df.to_csv('data.csv', index=False)
      print(df)
  
      df_error = df.copy()
  
      df_error['HumanError'] = abs(df_error['EngineScore'] - df_error['GameMoveScore'])
      df_error['NNUEError'] = abs(df_error['EngineScore'] - df_error['NNUEScore'])
      print(df_error)
  
      average_human_error = df_error['HumanError'].mean().round()
      average_nnue_error = df_error['NNUEError'].mean().round()
  
      print(f'AveHumanError: {average_human_error}')
      print(f'AveNNUEError: {average_nnue_error}')
  
  if __name__ == '__main__':
      main()
  
  ```
  
  Modify the stockfish and pgn paths to suit your needs. See,
  
  ```
  pgnfn = r'F:\project\pgn\tatamast23.pgn'
  enginefn = r'F:\Chess\Engines\stockfish\stockfish_15.1_win_x64_popcnt\stockfish-windows-2022-x86-64-modern.exe'
  
  ```
  
  **command line**
  
  ```
  python main.py >log.txt
  
  ```
  
  **output**
  
  ```
      GameNum                                                Epd  MoveNum                    Name GameMove  GameMoveScore EngineMove  EngineScore NNUEMove  NNUEScore  HumanError  NNUEError
  0         1  5rk1/1p1q1ppb/2p1p2p/4P3/R7/2Q3P1/1P2PPBP/6K1 ...       23         Carlsen, Magnus      Rd4             52        Rd4           52      Rd4         52           0          0
  1         1  5rk1/1pq2ppb/2p1p2p/4P3/3R4/2Q3P1/1P2PPBP/6K1 ...       24         Carlsen, Magnus      Rd6             62        Rd6           62      Rd7       -548           0        610
  2         1  5rk1/1pq2ppb/2pRp2p/4P3/8/2Q3P1/1P2PPBP/6K1 b - -       24          Aronian, Levon      Ra8            -73        Qb6          -51     Qxd6       -520          22        469
  3         1  r5k1/1pq2ppb/2pRp2p/4P3/8/2Q3P1/1P2PPBP/6K1 w - -       25         Carlsen, Magnus      Bf3             59         h4           69     Rxc6       -466          10        535
  4         1  r5k1/1pq2ppb/2pRp2p/4P3/8/2Q2BP1/1P2PP1P/6K1 b...       25          Aronian, Levon      Bg6            -38        Bg6          -38     Qxd6       -504           0        466
  ..      ...                                                ...      ...                     ...      ...            ...        ...          ...      ...        ...         ...        ...
  71        5                 8/p7/1p5R/3P1kp1/8/1K5P/r7/8 b - -       38  Abdusattorov, Nodirbek      Ra1             67        Rd2           67      Rd2         67           0          0
  72        5                 8/p7/1p5R/3P1kp1/8/1K5P/8/r7 w - -       39        Rapport, Richard      Kc4           -124        Kc2          -59      Kc4       -124          65         65
  73        5                  8/p7/7R/1pKP1kp1/8/7P/8/3r4 b - -       42  Abdusattorov, Nodirbek       b4            205         b4          205       a5         35           0        170
  74        5                 7R/p7/8/2KP1kp1/1p6/7P/8/3r4 b - -       43  Abdusattorov, Nodirbek       b3            205         b3          205       a5        103           0        102
  75        5               4R3/p7/8/2KP2p1/8/1p1k3P/8/3r4 w - -       46        Rapport, Richard       d6           -686        Rb8         -641       d6       -686          45         45
  
  [76 rows x 12 columns]
  AveHumanError: 8.0
  AveNNUEError: 251.0
  
  ```
  
  The function `nnue_eval()` will calculate the nnue eval of the legal moves from the given position or epd.
  
  I place the code in [github](https://github.com/fsmosca/nnue-strength) repository to easier maintain it.
  
  
  ---
  
  ### Answer by username_2 on 2023-04-13. Score: 3
  
  I don't think it's possible to isolate positional understanding from tactics completely when discussing human players.
  
  We can make some assessments when comparing engines among themselves. For instance, with the introduction of deep-learning-based engines, positional understanding has improved. But we can't really find an objective measurement of "positional strength" that would allow us to compare engines and humans.
  
  > **Comments:**
  > **username_0**: Do you mean that discussing positional strength is meaningless since it can't be objectively measured? I'm not asking for numbers, but understanding.  
  > **username_2**: @username_0 not ncessarely. There is no objective measurement but we can still make assessments like "Magnus Carlsen has a better positional understanding than you and I". The problem here is that the way engines think about chess is very different from that of humans, so in this case the comparison is harder to make.  
  
  ---
  
  ### Answer by username_3 on 2023-04-13. Score: 2
  
  The main things which stockfish considers when choosing a move are:
  
  * Material Imbalance
  * Positional Advantage
  * Material Advantage
  * Strategic Advantage for Pawns
  * Strategic Advantage for other Pieces
  * Incoming threats
  * Passed Pawns
  * Space
  * King Safety
  
  Nearly all of these things could be considered parts of "positional understanding" but to really understand how "strong" positional understanding is, looking for ideas and plans is necessary, stockfish does this using a optimized alphabeta pruning search, but this doesn't demonstrate any understanding of the board in front of it at all, so while stockfish with the ability to search has great ability to find and execute ideas, without that it has far less of an understanding than humans.
  
  To put it another way, humans try to come up with general plans and find good squares, while stockfish can place value on squares, it can't picture the pieces going to them without the ability to search, so its positional understanding in that case is really quite bad. I haven't tested it or seen data for it, but i would wager it is far below club players without search depth.
  
  > **Comments:**
  > **username_0**: Could you please explain what you mean by modern stockfish not using a neural net? I'm referring to the NNUE.  
  > **username_3**: my apologies, i misread an article, it does use a neural net, but not ordinary machine learning techniques. i'll correct it now  
  > **username_0**: Thanks for spending time on the answer. It seems though that "its positional understanding in that case is really quite bad" is mostly speculation? I'm also confused by your list, that seems to relate to pre-NNUE stockfish?  
  
  ---
  
  ### Answer by username_4 on 2023-04-16. Score: 0
  
  The idea that positional understanding and tactics are different is wrong. Even a beginner like myself needs both to play reasonably well. Without tactics I will never beat any decent player. Of course, being that this is Stockfish, it does a bit better than I would. Also, here are some in important things to consider in chess. They could be said to be either:
  
  1. **Threats**:You can make the arguement for both. Threats could be positional understanding. After all, they are something in your position that you need to understand (to avoid a blunder).
  2. **King safety**:Again, you can make the arguement for both. Understanding my position, I can realize that I must move my bishop to avoid mate in 1. Or I could say that I use tactics and realize that in order to win, I can't be checkmated, and reach the same conclusion
  3. **A strategic advantage of any kind**:The same as above. I can promote a pawn soon. I can checkmate soon. I can win a rook for a knight. Etc. I understand my position that I can promote a pawn. Or promoting that pawn is part of my tactics.
  4. **Etc**:Literally anything can be considered both. After all, everything a chess engine does is trying to win something. That is tactics. Also, there is no move that ignores the position, as that would be idiotic.
  
  So, to say how stong positional understanding is without tactics (or vice versa) is moot. Also consider the following opposite question. How strong is tactics without positional understanding? The answer. Nonexistent. How can one have tactics if you have no understanding of your position? You can't. How can one have positional understanding without any tactics? You can't.
  
  One other thing to note is that when the NNUE (Efficiently Updatable Neural Network) was added to Stockfish 12 it is increased the rating only by 100. While a bunch, it is not that large. This does indicate that Alpha Beta search was probably able to have positional understanding (because they are the same thing).
  
  > **Comments:**
  > **username_0**: There's plenty of chess literature that distinguishes positional understanding from tactics, so unless I'm misunderstanding you this argument seem a bit reductive?  
  > **username_4**: Really then name one concrete thing that is one and not the other? @username_0  
  
  ---
  
  ### Answer by username_5 on 2023-04-17. Score: 0
  
  # It depends
  
  Based on this [blog](https://blogs.cornell.edu/info2040/2022/09/30/game-theory-how-stockfish-mastered-chess/):
  
  > Stockfish uses a static evaluation function, or a function which calculates an estimate evaluation of the position using a heuristic (a manually constructed algorithm designed to fit any arbitrary chess position) which has been hand-crafted by computer scientists and chess grandmasters for over a decade. Since the evaluation function is very powerful we can assume it will do a good job most of the time to evaluate a position. However, this is not enough for a game as complex as chess – it is a Dynamic Game as opposed to a simultaneous-move game and involves niche game tree paths which can never be fully encapsulated in the problem space of an evaluation heuristic. There is a high probability that the function will evaluate a position as winning and miss a line or path of moves by the opponent which causes us to actually lose. It can also evaluate a position as losing and miss a line which is actually winning. The static evaluation function capitalizes on the deterministic nature of the game, however we need to do more to deal with the element of randomness. Hence, it is necessary for the engine to be able to calculate into the future.
  
  It states that depending on the position of the game, Stockfish when only using its static evaluation function can miss a far better outcome than what it plays and misevaluate the position. Therefore without the minimal and Alpha beta search methods, stockfish isn't considered very good when it comes to positional understanding, since it can misevaulate.
  
  > **Comments:**
  > **username_0**: Thanks for your answer. If I'm not misunderstanding, Stockfish does not use the static evaluation function anymore, but instead a neural network (the NNUE)? It is the positional understanding of this network that I'm wondering about.  
  > **username_4**: @username_0 is right, Stockfish uses a NNUE since stockfish 12  
  
  ---
  
  