Answers.Body: <p>Here is simple sample system using python script to analyze positions in a file with the engine and save the info to csv file.</p>
  <p>For larger number of positions you can save it in sqlite or mongodb and others for example. Code will be different.</p>
  <h3>Code</h3>
  <p>analysis.py</p>
  <pre><code>&quot;&quot;&quot;
  Analyze positions and save analysis.
  
  Requirements:
    pip install chess
    pip install pandas
  &quot;&quot;&quot;
  
  
  import chess.engine
  import pandas as pd
  
  
  headers = ['EPD', 'MOVE', 'SCORE', 'DEPTH', 'NODES', 'NPS', 'TIME', 'PV',
             'ENGINE', 'HASH', 'THREADS', 'CPU']
  
  
  def get_epd(epdfile):
      &quot;&quot;&quot;
      Read epdfile and return a list of positions.
      &quot;&quot;&quot;
      epds = []
      with open(epdfile, 'r') as f:
          for lines in f:
              epdline = lines.rstrip()
              epds.append(epdline)
      return epds
  
  
  def save(e, epdfile, dbfile, movetime):
      &quot;&quot;&quot;
      Analyze positions in epdfile with the engine and save the analysis in dbfile.
      &quot;&quot;&quot;
      data = []
      engine = chess.engine.SimpleEngine.popen_uci(e['enginefile'])
      engine.configure({'Hash': e['Hash']})
      engine.configure({'Threads': e['Threads']})
  
      epds = get_epd(epdfile)
  
      for epd in epds:
          board, _ = chess.Board.from_epd(epd)
          result = engine.analyse(
              board,
              chess.engine.Limit(time=movetime), game=object())
  
          move = result['pv'][0]
          pv = board.variation_san(result['pv'][:3])
          score = result['score'].relative.score(mate_score=32000)
          depth = result['depth']
          nodes = result['nodes']
          nps = result['nps']
          time = result['time']
  
          data.append(
              [epd, move, score, depth, nodes, nps, time, pv,
               e['enginename'], e['Hash'], e['Threads'], e['cpu']])
  
      engine.quit()
  
      df = pd.DataFrame(data)
      df.to_csv(dbfile, mode='a', index=False, header=None)
  
  
  def show_db(dbfile):
      df = pd.read_csv(dbfile, names=headers)
      df = df.reset_index(drop=True)
      print(df.to_string())
  
  
  def main():
      dbfile = 'db.csv'
      epdfile = &quot;positions.epd&quot;
      movetime = 2  # sec
  
      evaluator = [
          {
              'enginefile': 'stockfish_15_modern.exe',
              'enginename': 'Stockfish 15',
              'cpu': 'i7-2600',
              'Hash': 128,
              'Threads': 1
          },
          {
              'enginefile': 'komodo-12.1.1-64bit.exe',
              'enginename': 'Komodo 12',
              'cpu': 'i7-2600',
              'Hash': 128,
              'Threads': 2
          }
      ]
  
      # Analyze the positions in positions.epd with each engine configuration.
      for e in evaluator:
          save(e, epdfile, dbfile, movetime)
  
      show_db(dbfile)
  
  
  # Start
  main()
  </code></pre>
  <h3>Output</h3>
  <pre><code>                                                              EPD  MOVE  SCORE  DEPTH    NODES      NPS   TIME                PV        ENGINE  HASH  THREADS      CPU
  0     rnbqkb1r/1p3ppp/p2ppn2/8/2BNP3/2N5/PPP2PPP/R1BQK2R w KQkq -  a2a4     12     19  1219215   609302  2.001  1. a4 Nc6 2. Be3  Stockfish 15   128        1  i7-2600
  1         rnbqk2r/ppp1ppbp/6p1/8/3PP3/2P5/P4PPP/R1BQKBNR w KQkq -  g1f3     48     21  1287502   643429  2.001  1. Nf3 c5 2. Rb1  Stockfish 15   128        1  i7-2600
  2  r1bq1rk1/bpp2ppp/p1np1n2/4p3/4P3/1BPP1N2/PP1N1PPP/R1BQ1RK1 w -  h2h3     27     23  1331253   665293  2.001   1. h3 h6 2. Re1  Stockfish 15   128        1  i7-2600
  3     rnbqkb1r/1p3ppp/p2ppn2/8/2BNP3/2N5/PPP2PPP/R1BQK2R w KQkq -  e1g1     24     19  4787985  2390409  2.002  1. O-O b5 2. Bb3     Komodo 12   128        2  i7-2600
  4         rnbqk2r/ppp1ppbp/6p1/8/3PP3/2P5/P4PPP/R1BQKBNR w KQkq -  f1c4     50     18  5036475  2516976  2.001        1. Bc4 O-O     Komodo 12   128        2  i7-2600
  5  r1bq1rk1/bpp2ppp/p1np1n2/4p3/4P3/1BPP1N2/PP1N1PPP/R1BQ1RK1 w -  f1e1     39     20  5007664  2502586  2.000  1. Re1 Bd7 2. h3     Komodo 12   128        2  i7-2600
  </code></pre>
  <p>positions.epd</p>
  <pre><code>rnbqkb1r/1p3ppp/p2ppn2/8/2BNP3/2N5/PPP2PPP/R1BQK2R w KQkq -
  rnbqk2r/ppp1ppbp/6p1/8/3PP3/2P5/P4PPP/R1BQKBNR w KQkq -
  r1bq1rk1/bpp2ppp/p1np1n2/4p3/4P3/1BPP1N2/PP1N1PPP/R1BQ1RK1 w -
  </code></pre>
  
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2022-06-28T03:43:13.203Z
Answers.LastActivityDate: 2022-06-28T03:43:13.203Z
Answers.UserDisplayName: ferdy
Body: <p>I am looking for a tool that can store engine analysis of a chess positions database. It should be able to store multiple lines of analysis with complete info like depth, tablebase hits, score, etc.; the machine on which the analysis was done on including number of cores, what CPU, tablebases available, etc.; and the analysis of multiple engines for the same positions.</p>
  
Comments.ContentLicense: CC BY-SA 4.0
Comments.CreationDate: 2022-06-27T20:26:33.347Z
Comments.Text: What database system do you expect to store those positions etc.?
Comments.UserDisplayName: ferdy
Tags: <engines><analysis><database>
Title: Tool for storing multiple engines analysis of chess positions databse
ThreadText: # Tool for storing multiple engines analysis of chess positions databse
  
  (Asked by: username_0 on 2022-06-27)
  
  I am looking for a tool that can store engine analysis of a chess positions database. It should be able to store multiple lines of analysis with complete info like depth, tablebase hits, score, etc.; the machine on which the analysis was done on including number of cores, what CPU, tablebases available, etc.; and the analysis of multiple engines for the same positions.
  
  **Tags:** engines, analysis, database
  
  > **Comments:**
  > **username_1**: What database system do you expect to store those positions etc.?  
  
  ---
  
  ## 1 answers
  
  ### Answer by username_1 on 2022-06-28. Score: 4
  
  Here is simple sample system using python script to analyze positions in a file with the engine and save the info to csv file.
  
  For larger number of positions you can save it in sqlite or mongodb and others for example. Code will be different.
  
  ### Code
  
  analysis.py
  
  ```
  """
  Analyze positions and save analysis.
  
  Requirements:
    pip install chess
    pip install pandas
  """
  
  import chess.engine
  import pandas as pd
  
  headers = ['EPD', 'MOVE', 'SCORE', 'DEPTH', 'NODES', 'NPS', 'TIME', 'PV',
             'ENGINE', 'HASH', 'THREADS', 'CPU']
  
  def get_epd(epdfile):
      """
      Read epdfile and return a list of positions.
      """
      epds = []
      with open(epdfile, 'r') as f:
          for lines in f:
              epdline = lines.rstrip()
              epds.append(epdline)
      return epds
  
  def save(e, epdfile, dbfile, movetime):
      """
      Analyze positions in epdfile with the engine and save the analysis in dbfile.
      """
      data = []
      engine = chess.engine.SimpleEngine.popen_uci(e['enginefile'])
      engine.configure({'Hash': e['Hash']})
      engine.configure({'Threads': e['Threads']})
  
      epds = get_epd(epdfile)
  
      for epd in epds:
          board, _ = chess.Board.from_epd(epd)
          result = engine.analyse(
              board,
              chess.engine.Limit(time=movetime), game=object())
  
          move = result['pv'][0]
          pv = board.variation_san(result['pv'][:3])
          score = result['score'].relative.score(mate_score=32000)
          depth = result['depth']
          nodes = result['nodes']
          nps = result['nps']
          time = result['time']
  
          data.append(
              [epd, move, score, depth, nodes, nps, time, pv,
               e['enginename'], e['Hash'], e['Threads'], e['cpu']])
  
      engine.quit()
  
      df = pd.DataFrame(data)
      df.to_csv(dbfile, mode='a', index=False, header=None)
  
  def show_db(dbfile):
      df = pd.read_csv(dbfile, names=headers)
      df = df.reset_index(drop=True)
      print(df.to_string())
  
  def main():
      dbfile = 'db.csv'
      epdfile = "positions.epd"
      movetime = 2  # sec
  
      evaluator = [
          {
              'enginefile': 'stockfish_15_modern.exe',
              'enginename': 'Stockfish 15',
              'cpu': 'i7-2600',
              'Hash': 128,
              'Threads': 1
          },
          {
              'enginefile': 'komodo-12.1.1-64bit.exe',
              'enginename': 'Komodo 12',
              'cpu': 'i7-2600',
              'Hash': 128,
              'Threads': 2
          }
      ]
  
      # Analyze the positions in positions.epd with each engine configuration.
      for e in evaluator:
          save(e, epdfile, dbfile, movetime)
  
      show_db(dbfile)
  
  # Start
  main()
  
  ```
  
  ### Output
  
  ```
                                                                EPD  MOVE  SCORE  DEPTH    NODES      NPS   TIME                PV        ENGINE  HASH  THREADS      CPU
  0     rnbqkb1r/1p3ppp/p2ppn2/8/2BNP3/2N5/PPP2PPP/R1BQK2R w KQkq -  a2a4     12     19  1219215   609302  2.001  1. a4 Nc6 2. Be3  Stockfish 15   128        1  i7-2600
  1         rnbqk2r/ppp1ppbp/6p1/8/3PP3/2P5/P4PPP/R1BQKBNR w KQkq -  g1f3     48     21  1287502   643429  2.001  1. Nf3 c5 2. Rb1  Stockfish 15   128        1  i7-2600
  2  r1bq1rk1/bpp2ppp/p1np1n2/4p3/4P3/1BPP1N2/PP1N1PPP/R1BQ1RK1 w -  h2h3     27     23  1331253   665293  2.001   1. h3 h6 2. Re1  Stockfish 15   128        1  i7-2600
  3     rnbqkb1r/1p3ppp/p2ppn2/8/2BNP3/2N5/PPP2PPP/R1BQK2R w KQkq -  e1g1     24     19  4787985  2390409  2.002  1. O-O b5 2. Bb3     Komodo 12   128        2  i7-2600
  4         rnbqk2r/ppp1ppbp/6p1/8/3PP3/2P5/P4PPP/R1BQKBNR w KQkq -  f1c4     50     18  5036475  2516976  2.001        1. Bc4 O-O     Komodo 12   128        2  i7-2600
  5  r1bq1rk1/bpp2ppp/p1np1n2/4p3/4P3/1BPP1N2/PP1N1PPP/R1BQ1RK1 w -  f1e1     39     20  5007664  2502586  2.000  1. Re1 Bd7 2. h3     Komodo 12   128        2  i7-2600
  
  ```
  
  positions.epd
  
  ```
  rnbqkb1r/1p3ppp/p2ppn2/8/2BNP3/2N5/PPP2PPP/R1BQK2R w KQkq -
  rnbqk2r/ppp1ppbp/6p1/8/3PP3/2P5/P4PPP/R1BQKBNR w KQkq -
  r1bq1rk1/bpp2ppp/p1np1n2/4p3/4P3/1BPP1N2/PP1N1PPP/R1BQ1RK1 w -
  
  ```
  
  
  ---
  
  