{"id":38,"date":"2024-12-05T21:38:00","date_gmt":"2024-12-05T21:38:00","guid":{"rendered":"https:\/\/blog.chessboardmagic.com\/?p=38"},"modified":"2025-09-28T21:41:41","modified_gmt":"2025-09-28T21:41:41","slug":"chess-web-programming-part-five-game-review","status":"publish","type":"post","link":"https:\/\/blog.chessboardmagic.com\/index.php\/2024\/12\/05\/chess-web-programming-part-five-game-review\/","title":{"rendered":"Chess Web Programming: Part Five: Game Review"},"content":{"rendered":"\n<p><strong>Creating a Game Review Application<\/strong><\/p>\n\n\n\n<p>Building chess web applications has been a fun and challenging journey, combining the strategies of chess with the world of web programming. Since starting this project, I\u2019ve watched&nbsp;<strong><a href=\"https:\/\/chessboardmagic.com\/\">Chessboard Magic<\/a><\/strong>&nbsp;grow from a simple app into a platform with 34 unique features that make learning and playing chess more interactive and enjoyable.<br>In my previous blogs, I\u2019ve broken down the steps for anyone interested in building their own chess web app:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong><a href=\"https:\/\/lichess.org\/@\/HollowLeaf\/blog\/chess-web-programming-part-one-getting-started\/8ZKpwJU8\">Part One: Getting Started<\/a><\/strong>\u00a0\u2013 We created a basic chessboard, allowing users to move pieces and setting up the foundation for managing game states and interactions.<\/li>\n\n\n\n<li><strong><a href=\"https:\/\/lichess.org\/@\/HollowLeaf\/blog\/chess-web-programming-part-two-stockfish\/PdeOTODf\">Part Two: Adding Stockfish<\/a><\/strong>\u00a0\u2013 We integrated the Stockfish chess engine to analyze moves and provide insights, making the app engaging and interactive for users looking to deepen their understanding of chess.<\/li>\n\n\n\n<li><strong><a href=\"https:\/\/lichess.org\/@\/HollowLeaf\/blog\/chess-web-programming-part-three-deploying-your-application\/J3GdsKZP\">Part Three: Deploying Your Application<\/a><\/strong>\u00a0\u2013 We covered how to deploy the app using GitHub Pages, allowing anyone to share their chessboard online. This part included setup instructions for Git, GitHub, and deployment configurations to make the app live.<\/li>\n\n\n\n<li><strong><a href=\"https:\/\/lichess.org\/@\/HollowLeaf\/blog\/chess-web-programming-part-four-chessboard-customisation\/Is0jxElj\">Part Four: Customization<\/a><\/strong>\u00a0\u2013 We added customizations for the chessboard, allowing users to switch between different piece sets and board themes. These changes made the chessboard visually unique and gave users more control over their experience.<\/li>\n<\/ul>\n\n\n\n<p>Each part offers straightforward steps and practical tips to help readers build and enhance their chess applications.<br>In this fifth part,&nbsp;<strong>Game Review<\/strong>, we\u2019ll dive into building a game review feature by adding engine lines and a move categorization tool. This feature will assess the quality of each move, highlighting whether it was strong or weak, and providing players with insights to help them learn from each game.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><a href=\"https:\/\/lichess.org\/@\/HollowLeaf\/blog\/chess-web-programming-part-five-game-review\/32JsEsrs#setting-up-the-react-application\"><\/a>Setting Up the React Application<\/h2>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p><strong>Note<\/strong>: This guide assumes you\u2019ve installed the necessary software and tools covered in the previous blogs, including Node.js, npm, and any preferred code editor like VSCode. If not, please refer to earlier parts of this series for guidance on installing these tools.<\/p>\n<\/blockquote>\n\n\n\n<p>In this section, we\u2019ll start by setting up our React application, which we\u2019ll name&nbsp;<strong>gamereview<\/strong>. This will serve as the foundation for building our game review application, where we\u2019ll add a move review feature to analyze chess games. Let\u2019s get started!<\/p>\n\n\n\n<h4 class=\"wp-block-heading\"><a href=\"https:\/\/lichess.org\/@\/HollowLeaf\/blog\/chess-web-programming-part-five-game-review\/32JsEsrs#1-create-the-react-application\"><\/a><strong>1. Create the React Application<\/strong><\/h4>\n\n\n\n<p>First, we need to create our React project. Open your terminal and run the following command:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>npx create-react-app gamereview<\/code><\/pre>\n\n\n\n<p>This will set up a basic React app with everything we need to get started.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\"><a href=\"https:\/\/lichess.org\/@\/HollowLeaf\/blog\/chess-web-programming-part-five-game-review\/32JsEsrs#2-navigate-to-the-project-folder\"><\/a><strong>2. Navigate to the Project Folder<\/strong><\/h4>\n\n\n\n<p>Once the setup is complete, navigate to the new project folder:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>cd gamereview<\/code><\/pre>\n\n\n\n<h4 class=\"wp-block-heading\"><a href=\"https:\/\/lichess.org\/@\/HollowLeaf\/blog\/chess-web-programming-part-five-game-review\/32JsEsrs#3-install-required-dependencies\"><\/a><strong>3. Install Required Dependencies<\/strong><\/h4>\n\n\n\n<p>We\u2019ll be using two main libraries to help us build the game review feature:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>react-chessboard<\/strong>: A React component for displaying an interactive chessboard.<\/li>\n\n\n\n<li><strong>chess.js<\/strong>: A library that allows us to manage chess game logic, including move validation, piece movement, and move history.<\/li>\n<\/ul>\n\n\n\n<p>To install these libraries, run the following command in the terminal:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>npm install react-chessboard chess.js<\/code><\/pre>\n\n\n\n<h4 class=\"wp-block-heading\"><a href=\"https:\/\/lichess.org\/@\/HollowLeaf\/blog\/chess-web-programming-part-five-game-review\/32JsEsrs#4-add-the-folder-to-your-workspace-in-vs-code\"><\/a><strong>4. Add the Folder to Your Workspace in VS Code<\/strong><\/h4>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Open\u00a0<strong>Visual Studio Code<\/strong>.<\/li>\n\n\n\n<li>Go to\u00a0<strong>File<\/strong>\u00a0>\u00a0<strong>Add Folder to Workspace&#8230;.<\/strong><\/li>\n\n\n\n<li>Select the\u00a0<code>gamereview<\/code>\u00a0folder you just created.<\/li>\n\n\n\n<li>This will add the project to your workspace, allowing you to view and edit all files within VS Code easily.<\/li>\n<\/ul>\n\n\n\n<h4 class=\"wp-block-heading\"><a href=\"https:\/\/lichess.org\/@\/HollowLeaf\/blog\/chess-web-programming-part-five-game-review\/32JsEsrs#5-verify-the-installation\"><\/a><strong>5. Verify the Installation<\/strong><\/h4>\n\n\n\n<p>After installing the dependencies, verify that they\u2019re listed in your&nbsp;<code>package.json<\/code>&nbsp;file under dependencies. Your&nbsp;<code>package.json<\/code>&nbsp;should include the following:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>{\n  \"dependencies\": {\n    \"react\": \"^18.0.0\",\n    \"react-dom\": \"^18.0.0\",\n    \"react-scripts\": \"5.0.1\",\n    \"react-chessboard\": \"^1.2.0\",\n    \"chess.js\": \"^1.0.0\"\n  }\n}<\/code><\/pre>\n\n\n\n<h4 class=\"wp-block-heading\"><a href=\"https:\/\/lichess.org\/@\/HollowLeaf\/blog\/chess-web-programming-part-five-game-review\/32JsEsrs#6-create-a-basic-chessboard-component-in-appjs\"><\/a><strong>6. Create a Basic Chessboard Component in&nbsp;<code>App.js<\/code><\/strong><\/h4>\n\n\n\n<p>Now, let\u2019s set up a basic chessboard using&nbsp;<code>react-chessboard<\/code>&nbsp;and add an&nbsp;<code>onDrop<\/code>&nbsp;function to validate moves.<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Open\u00a0<code>App.js<\/code><\/strong>: In your project folder, open the\u00a0<code>App.js<\/code>\u00a0file. By default, it will contain some boilerplate code from the React setup. We\u2019ll replace this with a simple chessboard setup.<\/li>\n\n\n\n<li><strong>Import Dependencies<\/strong>: First, import the necessary components from\u00a0<code>react-chessboard<\/code>\u00a0and\u00a0<code>chess.js<\/code>\u00a0at the top of the file.<\/li>\n<\/ul>\n\n\n\n<pre class=\"wp-block-code\"><code>import React, { useState } from \"react\";\nimport { Chessboard } from \"react-chessboard\";\nimport Chess from \"chess.js\";<\/code><\/pre>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Set Up the Chessboard Component with\u00a0<code>onDrop<\/code><\/strong>: Next, we\u2019ll initialize a new\u00a0<code>Chess<\/code>\u00a0instance and set up an\u00a0<code>onDrop<\/code>\u00a0function to validate moves using a try-catch block. This function will try to make a move and catch any errors if the move is invalid.<\/li>\n<\/ul>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/ Main App component\nconst App = () => {\n  \/\/ Initialize the game state using useState with a new Chess instance\n  const &#91;game, setGame] = useState(new Chess());\n\n  \/\/ Function to handle piece movement on the chessboard\n  const onDrop = (sourceSquare, targetSquare) => {\n    \/\/ Create a copy of the current game state using FEN notation\n    const gameCopy = new Chess(game.fen());\n\n    try {\n      \/\/ Attempt to make the move on the game copy\n      const move = gameCopy.move({\n        from: sourceSquare, \/\/ Starting square of the move\n        to: targetSquare, \/\/ Target square of the move\n        promotion: \"q\", \/\/ Always promote to a queen for simplicity\n      });\n\n      \/\/ If the move is invalid, move will be null, so we return false to ignore the move\n      if (move === null) {\n        return false;\n      }\n\n      \/\/ If the move is valid, update the game state with the new position\n      setGame(gameCopy);\n      return true; \/\/ Return true to indicate a valid move\n    } catch (error) {\n      \/\/ Catch and log any errors that occur during the move attempt\n      console.error(error.message);\n      return false; \/\/ Return false to ignore the invalid move\n    }\n  };\n\n  return (\n    &lt;div>\n      &lt;h1>Game Review&lt;\/h1>\n      &lt;Chessboard\n        position={game.fen()} \/\/ Set the chessboard position to the current game state\n        onPieceDrop={onDrop} \/\/ Trigger the onDrop function when a piece is moved\n        boardWidth={500} \/\/ Set the width of the chessboard to 500px\n      \/>\n    &lt;\/div>\n  );\n};\n\nexport default App; \/\/ Export the App component as the default export<\/code><\/pre>\n\n\n\n<p><strong>7. Run the Application<\/strong><br>Now, go back to your terminal and start the React development server:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>npm start<\/code><\/pre>\n\n\n\n<p>This command will open your application in the browser at&nbsp;<code>http:\/\/localhost:3000<\/code>. You should see a chessboard centered on the screen with a width of 500px. You can now drag and drop pieces, and the&nbsp;<code>onDrop<\/code>&nbsp;function will validate moves using&nbsp;<code>chess.js<\/code>. If a move is invalid, it will be prevented, and an error will be logged in the console.<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p><strong>Note<\/strong>: The basics of setting up a React application with a chessboard component and validating moves were covered in depth in&nbsp;<strong>Part One<\/strong>&nbsp;of this series.<\/p>\n<\/blockquote>\n\n\n\n<h2 class=\"wp-block-heading\"><a href=\"https:\/\/lichess.org\/@\/HollowLeaf\/blog\/chess-web-programming-part-five-game-review\/32JsEsrs#integrating-stockfish\"><\/a>Integrating Stockfish<\/h2>\n\n\n\n<p>In this section, we\u2019ll integrate Stockfish, a powerful chess engine, into our React chessboard. Stockfish can evaluate each move and provide feedback on the top 3 recommended moves. This feature will allow users to see the best moves in each position, helping them improve their gameplay.<br>Let\u2019s walk through each step to set up Stockfish.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\"><a href=\"https:\/\/lichess.org\/@\/HollowLeaf\/blog\/chess-web-programming-part-five-game-review\/32JsEsrs#1-download-stockfish-lite\"><\/a>1. Download Stockfish Lite<\/h4>\n\n\n\n<p>First, download&nbsp;<code>stockfish-16.1-lite-single.js<\/code>&nbsp;and&nbsp;<code>stockfish-16.1-lite-single.wasm<\/code>&nbsp;from the&nbsp;<a href=\"https:\/\/github.com\/nmrugg\/stockfish.js\/tree\/master\/src\">Stockfish.js GitHub repository<\/a>. These files contain a lightweight version of the Stockfish engine designed to work in web applications.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\"><a href=\"https:\/\/lichess.org\/@\/HollowLeaf\/blog\/chess-web-programming-part-five-game-review\/32JsEsrs#2-add-stockfish-files-to-publicjs\"><\/a>2. Add Stockfish Files to&nbsp;<code>public\/js<\/code><\/h4>\n\n\n\n<p>Once downloaded, create a&nbsp;<code>js<\/code>&nbsp;folder within the&nbsp;<code>public<\/code>&nbsp;directory of your React project. Copy&nbsp;<code>stockfish-16.1-lite-single.js<\/code>&nbsp;and&nbsp;<code>stockfish-16.1-lite-single.wasm<\/code>&nbsp;into&nbsp;<code>public\/js<\/code>. Your project structure should now look like this:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>gamereview\n public\n    js\n       stockfish-16.1-lite-single.js\n       stockfish-16.1-lite-single.wasm\n    index.html\n src<\/code><\/pre>\n\n\n\n<p>This setup will allow us to reference Stockfish directly from the&nbsp;<code>\/js<\/code>&nbsp;folder within our React app.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\"><a href=\"https:\/\/lichess.org\/@\/HollowLeaf\/blog\/chess-web-programming-part-five-game-review\/32JsEsrs#3-load-stockfish-in-appjs\"><\/a>3. Load Stockfish in&nbsp;<code>App.js<\/code><\/h4>\n\n\n\n<p>To load Stockfish in our application, we\u2019ll create a Web Worker for&nbsp;<code>stockfish-16.1-lite-single.js<\/code>. This allows Stockfish to run in a separate thread so it doesn\u2019t slow down our main application.<br>Here\u2019s the code to set up Stockfish in&nbsp;<code>App.js<\/code>:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>const App = () => {\n  const &#91;stockfish, setStockfish] = useState(null);\n\n  \/\/ Load Stockfish using useEffect when the component mounts\n  useEffect(() => {\n    \/\/ Create a new Web Worker for Stockfish\n    const stockfishInstance = new Worker(`${process.env.PUBLIC_URL}\/js\/stockfish-16.1-lite-single.js`);\n\n    setStockfish(stockfishInstance); \/\/ Store the Stockfish instance in state\n\n    \/\/ Clean up by terminating the Stockfish instance when the component unmounts\n    return () => {\n      stockfishInstance.terminate();\n    };\n  }, &#91;]);\n};<\/code><\/pre>\n\n\n\n<p><strong>Explanation<\/strong>:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Creating the Worker<\/strong>:\u00a0<code>const stockfishInstance = new Worker(\"\/js\/stockfish-16.1-lite-single.js\");<\/code>\u00a0loads Stockfish as a Web Worker from the\u00a0<code>\/js<\/code>\u00a0directory.<\/li>\n\n\n\n<li><strong>Setting State<\/strong>: We store this Stockfish instance in a\u00a0<code>useState<\/code>\u00a0variable, making it accessible across the component.<\/li>\n\n\n\n<li><strong>Cleanup<\/strong>: The\u00a0<code>return<\/code>\u00a0function in\u00a0<code>useEffect<\/code>\u00a0ensures that Stockfish is terminated when the component unmounts, freeing up resources.<\/li>\n<\/ul>\n\n\n\n<h4 class=\"wp-block-heading\"><a href=\"https:\/\/lichess.org\/@\/HollowLeaf\/blog\/chess-web-programming-part-five-game-review\/32JsEsrs#4-write-the-evaluation-function\"><\/a>4. Write the Evaluation Function<\/h4>\n\n\n\n<p>The&nbsp;<code>getEvaluation<\/code>&nbsp;function will set up Stockfish to evaluate each board position:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>const getEvaluation = (fen) => {\n  return new Promise((resolve) => {\n    const lines = &#91;]; \/\/ Array to store the top 3 lines of evaluations\n    stockfish.postMessage(\"setoption name MultiPV value 3\"); \/\/ Set Stockfish to calculate top 3 PVs\n    stockfish.postMessage(`position fen ${fen}`); \/\/ Set the position to the current FEN\n    stockfish.postMessage(\"go depth 12\"); \/\/ Instruct Stockfish to calculate up to a depth of 12\n\n    const isBlackTurn = fen.split(\" \")&#91;1] === \"b\"; \/\/ Check if it's Black's turn from the FEN string\n\n    \/\/ Save the current lines as previous lines before starting a new evaluation\n    setPreviousLines(currentLines);\n\n    \/\/ Handle messages from Stockfish\n    stockfish.onmessage = (event) => {\n      const message = event.data;\n\n      \/\/ Only process messages that contain evaluations at depth 12\n      if (message.startsWith(\"info depth 12\")) {\n        \/\/ Extract the evaluation score and principal variation (move sequence)\n        const match = message.match(\/score cp (-?\\d+).* pv (.+)\/);\n        if (match) {\n          let evalScore = parseInt(match&#91;1], 10) \/ 100; \/\/ Convert centipawn score to pawn units\n          const moves = match&#91;2].split(\" \"); \/\/ Split moves into an array\n\n          \/\/ Flip the evaluation score if it's Black's turn\n          if (isBlackTurn) {\n            evalScore = -evalScore;\n          }\n\n          \/\/ Add the evaluation and moves to the lines array\n          lines.push({ eval: evalScore, moves });\n\n          \/\/ Stop and resolve once we have the top 3 lines at depth 12\n          if (lines.length === 3) {\n            stockfish.postMessage(\"stop\"); \/\/ Stop Stockfish once we have 3 evaluations\n\n            \/\/ Sort lines based on whose turn it is\n            lines.sort((a, b) => (isBlackTurn ? a.eval - b.eval : b.eval - a.eval));\n\n            \/\/ Update currentLines with the new sorted evaluations\n            setCurrentLines(lines);\n            resolve(lines); \/\/ Resolve the promise with the top 3 lines\n          }\n        }\n      }\n    };\n  });\n};<\/code><\/pre>\n\n\n\n<p><strong>Explanation<\/strong>:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Setting MultiPV<\/strong>:\u00a0<code>MultiPV<\/code>\u00a0is set to 3 to request the top 3 moves from Stockfish.<\/li>\n\n\n\n<li><strong>Setting Position and Depth<\/strong>: We set the FEN and depth of 12, instructing Stockfish to analyze the position deeply.<\/li>\n\n\n\n<li><strong>Handling Stockfish Output<\/strong>:\n<ul class=\"wp-block-list\">\n<li>We listen for\u00a0<code>info depth 12<\/code>\u00a0messages from Stockfish and parse these for evaluation scores and moves.<\/li>\n\n\n\n<li>If it\u2019s Black\u2019s turn, we adjust the evaluation score by multiplying it by\u00a0<code>-1<\/code>.<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li><strong>Updating State<\/strong>: Once 3 evaluations are collected, we stop Stockfish and update\u00a0<code>currentLines<\/code>\u00a0with the sorted lines.<\/li>\n<\/ul>\n\n\n\n<h4 class=\"wp-block-heading\"><a href=\"https:\/\/lichess.org\/@\/HollowLeaf\/blog\/chess-web-programming-part-five-game-review\/32JsEsrs#full-code\"><\/a>Full Code<\/h4>\n\n\n\n<p>Here\u2019s the complete&nbsp;<code>App.js<\/code>&nbsp;file with full comments, which concludes the&nbsp;<strong>Integrating Stockfish<\/strong>&nbsp;section:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>import React, { useState, useEffect } from \"react\";\nimport { Chessboard } from \"react-chessboard\";\nimport { Chess } from \"chess.js\";\n\nconst App = () => {\n  \/\/ Initialize chess game state and Stockfish instance\n  const &#91;game, setGame] = useState(new Chess());\n  const &#91;stockfish, setStockfish] = useState(null);\n  const &#91;currentLines, setCurrentLines] = useState(&#91;]); \/\/ Holds evaluations for the current move\n  const &#91;previousLines, setPreviousLines] = useState(&#91;]); \/\/ Holds evaluations for the previous move\n\n  \/\/ Load Stockfish Web Worker using useEffect when the component mounts\n  useEffect(() => {\n    \/\/ Create a new Web Worker for Stockfish\n    const stockfishInstance = new Worker(`${process.env.PUBLIC_URL}\/js\/stockfish-16.1-lite-single.js`);\n\n    setStockfish(stockfishInstance); \/\/ Store the Stockfish instance in state\n\n    \/\/ Clean up by terminating the Stockfish instance when the component unmounts\n    return () => {\n      stockfishInstance.terminate();\n    };\n  }, &#91;]);\n\n  \/\/ Function to get top 3 evaluations and moves from Stockfish at a depth of 12\n  const getEvaluation = (fen) => {\n    return new Promise((resolve) => {\n      const lines = &#91;]; \/\/ Array to store the top 3 lines of evaluations\n      stockfish.postMessage(\"setoption name MultiPV value 3\"); \/\/ Set Stockfish to calculate top 3 PVs\n      stockfish.postMessage(`position fen ${fen}`); \/\/ Set the position to the current FEN\n      stockfish.postMessage(\"go depth 12\"); \/\/ Instruct Stockfish to calculate up to a depth of 12\n\n      const isBlackTurn = fen.split(\" \")&#91;1] === \"b\"; \/\/ Check if it's Black's turn from the FEN string\n\n      \/\/ Save the current lines as previous lines before starting a new evaluation\n      setPreviousLines(currentLines);\n\n      \/\/ Handle messages from Stockfish\n      stockfish.onmessage = (event) => {\n        const message = event.data;\n\n        \/\/ Only process messages that contain evaluations at depth 12\n        if (message.startsWith(\"info depth 12\")) {\n          \/\/ Extract the evaluation score and principal variation (move sequence)\n          const match = message.match(\/score cp (-?\\d+).* pv (.+)\/);\n          if (match) {\n            let evalScore = parseInt(match&#91;1], 10) \/ 100; \/\/ Convert centipawn score to pawn units\n            const moves = match&#91;2].split(\" \"); \/\/ Split moves into an array\n\n            \/\/ Flip the evaluation score if it's Black's turn\n            if (isBlackTurn) {\n              evalScore = -evalScore;\n            }\n\n            \/\/ Add the evaluation and moves to the lines array\n            lines.push({ eval: evalScore, moves });\n\n            \/\/ Stop and resolve once we have the top 3 lines at depth 12\n            if (lines.length === 3) {\n              stockfish.postMessage(\"stop\"); \/\/ Stop Stockfish once we have 3 evaluations\n\n              \/\/ Sort lines based on whose turn it is\n              lines.sort((a, b) => (isBlackTurn ? a.eval - b.eval : b.eval - a.eval));\n\n              \/\/ Update currentLines with the new sorted evaluations\n              setCurrentLines(lines);\n              resolve(lines); \/\/ Resolve the promise with the top 3 lines\n            }\n          }\n        }\n      };\n    });\n  };\n\n  \/\/ onDrop function to handle piece movement and trigger Stockfish evaluation\n  const onDrop = async (sourceSquare, targetSquare) => {\n    const gameCopy = new Chess(game.fen()); \/\/ Create a copy of the current game\n\n    try {\n      \/\/ Attempt to make the move on the game copy\n      const move = gameCopy.move({\n        from: sourceSquare,\n        to: targetSquare,\n        promotion: \"q\", \/\/ Automatically promote to a queen for simplicity\n      });\n\n      if (move === null) return false; \/\/ If move is invalid, return false\n\n      setGame(gameCopy); \/\/ Update the game state with the new move\n\n      \/\/ Get top 3 moves and evaluations from Stockfish at depth 12\n      await getEvaluation(gameCopy.fen());\n      return true;\n    } catch (error) {\n      console.error(error.message); \/\/ Log any errors during move attempt\n      return false;\n    }\n  };\n\n  return (\n    &lt;div>\n      &lt;h1>Game Review with Stockfish&lt;\/h1>\n      {\/* Chessboard component to display the game board *\/}\n      &lt;Chessboard\n        position={game.fen()} \/\/ Set the chessboard position to the current game state\n        onPieceDrop={onDrop} \/\/ Trigger onDrop function when a piece is moved\n        boardWidth={500} \/\/ Set the width of the chessboard to 500px\n      \/>\n\n      {\/* Display the top 3 evaluation lines *\/}\n      &lt;div>\n        &lt;h2>Top 3 Lines at Depth 12&lt;\/h2>\n        &lt;ul style={{ listStyleType: \"none\", paddingLeft: 0 }}>\n          {currentLines.map((line, index) => (\n            &lt;li key={index} style={{ marginBottom: \"10px\" }}>\n              &lt;strong>Line {index + 1}:&lt;\/strong> {line.eval} &lt;br \/>\n              &lt;strong>Moves:&lt;\/strong> {line.moves.join(\" \")}\n            &lt;\/li>\n          ))}\n        &lt;\/ul>\n      &lt;\/div>\n    &lt;\/div>\n  );\n};\n\nexport default App;<\/code><\/pre>\n\n\n\n<p>With this setup, Stockfish will evaluate each move, displaying the top 3 recommendations with evaluations below the chessboard. This enables users to explore the best moves and improve their understanding of the game.<br>You should now see the following:<br><img decoding=\"async\" src=\"https:\/\/image.lichess1.org\/display?fmt=webp&amp;h=0&amp;op=resize&amp;path=hollowleaf:ublogBody:Am9BN76H1ois:fgcCMTv5.png&amp;w=800&amp;sig=48f1bd9280bc9e6742d3b2cad50bc7edfd6f6864\" alt=\"image.png\"><\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><a href=\"https:\/\/lichess.org\/@\/HollowLeaf\/blog\/chess-web-programming-part-five-game-review\/32JsEsrs#showing-move-category\"><\/a>Showing Move Category<\/h2>\n\n\n\n<p>In this section, we\u2019ll extend our Chessboard application to categorize each move based on how well it aligns with the best moves according to Stockfish. We&#8217;ll break down each step, making it easy to understand how we\u2019re adding the \u201cmove category\u201d feature to show insights into each move.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><a href=\"https:\/\/lichess.org\/@\/HollowLeaf\/blog\/chess-web-programming-part-five-game-review\/32JsEsrs#step-1-set-up-state-for-move-analysis\"><\/a>Step 1: Set Up State for Move Analysis<\/h3>\n\n\n\n<p>To support move categorization, we\u2019ll introduce three new&nbsp;<code>useState<\/code>&nbsp;hooks:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong><code>bestEvaluation<\/code><\/strong>\u00a0&#8211; Stores the evaluation of the top move from Stockfish.<\/li>\n\n\n\n<li><strong><code>lastMove<\/code><\/strong>\u00a0&#8211; Saves the most recent move played by the user.<\/li>\n\n\n\n<li><strong><code>moveCategory<\/code><\/strong>\u00a0&#8211; Holds the category label for the player\u2019s move.<\/li>\n<\/ol>\n\n\n\n<p>Add the following code at the beginning of your component:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>const &#91;bestEvaluation, setBestEvaluation] = useState(null); \/\/ Holds the eval of Stockfish's best move\nconst &#91;lastMove, setLastMove] = useState(null); \/\/ Stores the player's last move\nconst &#91;moveCategory, setMoveCategory] = useState(\"\"); \/\/ Stores the category label of the move<\/code><\/pre>\n\n\n\n<p>These new states will help us keep track of essential information for categorizing the player\u2019s move.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><a href=\"https:\/\/lichess.org\/@\/HollowLeaf\/blog\/chess-web-programming-part-five-game-review\/32JsEsrs#step-2-modify-getevaluation-to-save-the-best-move-evaluation\"><\/a>Step 2: Modify&nbsp;<code>getEvaluation<\/code>&nbsp;to Save the Best Move Evaluation<\/h3>\n\n\n\n<p>The&nbsp;<code>getEvaluation<\/code>&nbsp;function gathers evaluations and moves from Stockfish. We\u2019ll modify it to store only the evaluation score of the top recommended move in&nbsp;<code>bestEvaluation<\/code>. This way, we can later use&nbsp;<code>bestEvaluation<\/code>&nbsp;to determine if the player\u2019s move was optimal or not.<br>Add the following inside&nbsp;<code>getEvaluation<\/code>&nbsp;after updating&nbsp;<code>currentLines<\/code>:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>setBestEvaluation(lines&#91;0].eval); \/\/ Set bestEvaluation to the eval value of the top move<\/code><\/pre>\n\n\n\n<p>This stores only the numerical evaluation of Stockfish\u2019s best move for easy comparison in the next steps.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><a href=\"https:\/\/lichess.org\/@\/HollowLeaf\/blog\/chess-web-programming-part-five-game-review\/32JsEsrs#step-3-save-lastmove-in-ondrop\"><\/a>Step 3: Save&nbsp;<code>lastMove<\/code>&nbsp;in&nbsp;<code>onDrop<\/code><\/h3>\n\n\n\n<p>To keep track of the player\u2019s most recent move, update the&nbsp;<code>onDrop<\/code>&nbsp;function to save&nbsp;<code>lastMove<\/code>&nbsp;every time a new move is made.<br>Update&nbsp;<code>onDrop<\/code>&nbsp;with the following line:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>setLastMove(`${sourceSquare}${targetSquare}`); \/\/ Save the last move<\/code><\/pre>\n\n\n\n<p>This code converts the move coordinates (like&nbsp;<code>e2<\/code>&nbsp;to&nbsp;<code>e4<\/code>) into a single string and stores it in&nbsp;<code>lastMove<\/code>.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><a href=\"https:\/\/lichess.org\/@\/HollowLeaf\/blog\/chess-web-programming-part-five-game-review\/32JsEsrs#step-4-update-previouslines\"><\/a>Step 4: Update&nbsp;<code>PreviousLines<\/code><\/h3>\n\n\n\n<p>To keep track of the previousLines, we will update this useState before updating the currentLines in the getEvaluation function. By doing this way we have a way to look back one move.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>setPreviousLines(currentLines); \/\/ Added\nsetCurrentLines(lines);<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\"><a href=\"https:\/\/lichess.org\/@\/HollowLeaf\/blog\/chess-web-programming-part-five-game-review\/32JsEsrs#step-5-create-getmovecategory-to-classify-the-players-move\"><\/a>Step 5: Create&nbsp;<code>getMoveCategory<\/code>&nbsp;to Classify the Player\u2019s Move<\/h3>\n\n\n\n<p>We\u2019ll add a new function called&nbsp;<code>getMoveCategory<\/code>&nbsp;to categorize the player\u2019s move. The function will classify the move based on whether it matches Stockfish\u2019s top recommendations or how it deviates from the best move\u2019s evaluation score.<br><strong>Why Use&nbsp;<code>useCallback<\/code>?<\/strong><br>By using&nbsp;<code>useCallback<\/code>, we can ensure that&nbsp;<code>getMoveCategory<\/code>&nbsp;is only recreated when its dependencies (<code>bestEvaluation<\/code>,&nbsp;<code>lastMove<\/code>, and&nbsp;<code>previousLines<\/code>) change. This helps improve performance by avoiding unnecessary recalculations and re-renders whenever the component updates.<br><strong>Importing&nbsp;<code>useCallback<\/code><\/strong><br>To get started, make sure&nbsp;<code>useCallback<\/code>&nbsp;is imported from React at the top of your file. You can import it along with other hooks like this:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>import React, { useState, useEffect, useCallback } from \"react\";<\/code><\/pre>\n\n\n\n<p><strong>Writing the&nbsp;<code>getMoveCategory<\/code>&nbsp;Function<\/strong><br>Now, define&nbsp;<code>getMoveCategory<\/code>&nbsp;using&nbsp;<code>useCallback<\/code>&nbsp;to optimize its performance. Here\u2019s how the function will look:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>const getMoveCategory = useCallback(() => {\n  const previousTopLine = previousLines&#91;0];\n  const previousSecondLine = previousLines&#91;1];\n  const previousThirdLine = previousLines&#91;2];\n\n  \/\/ If any required data is missing, reset move category\n  if (!bestEvaluation || !lastMove || !previousTopLine) {\n    setMoveCategory(\"\");\n    return;\n  }\n\n  \/\/ Get the best moves from Stockfish's previous evaluations\n  const previousTopMove = previousTopLine?.moves&#91;0];\n  const previousSecondMove = previousSecondLine?.moves&#91;0];\n  const previousThirdMove = previousThirdLine?.moves&#91;0];\n\n  \/\/ Categorize move based on whether it matches the best moves or evaluation difference\n  if (lastMove === previousTopMove) {\n    setMoveCategory(\"Top\");\n  } else if (lastMove === previousSecondMove || lastMove === previousThirdMove) {\n    setMoveCategory(\"Good Move\");\n  } else {\n    const evaluationDifference = Math.abs(bestEvaluation - previousTopLine.eval);\n\n    \/\/ Set move category based on evaluation difference thresholds\n    if (evaluationDifference &lt;= 1) {\n      setMoveCategory(\"Ok\");\n    } else if (evaluationDifference &lt;= 2) {\n      setMoveCategory(\"Inaccuracy\");\n    } else if (evaluationDifference >= 3) {\n      setMoveCategory(\"Blunder\");\n    }\n  }\n}, &#91;bestEvaluation, lastMove, previousLines]);<\/code><\/pre>\n\n\n\n<p>The&nbsp;<code>getMoveCategory<\/code>&nbsp;function is designed to analyze the player\u2019s last move and categorize it based on how well it aligns with Stockfish\u2019s top recommended moves. Here\u2019s a step-by-step breakdown of the logic:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>Extract Top Recommended Moves<\/strong>:\n<ul class=\"wp-block-list\">\n<li>The function begins by retrieving the top three moves (or \u201clines\u201d) from Stockfish\u2019s previous evaluations, stored in\u00a0<code>previousLines<\/code>.<\/li>\n\n\n\n<li><code>previousTopLine<\/code>\u00a0represents Stockfish\u2019s best recommendation, while\u00a0<code>previousSecondLine<\/code>\u00a0and\u00a0<code>previousThirdLine<\/code>\u00a0represent the second and third best moves, respectively.<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li><strong>Check for Required Data<\/strong>:\n<ul class=\"wp-block-list\">\n<li>If any of the key data (best evaluation score, the last move made, or the top recommendation) is missing, the function exits and resets the\u00a0<code>moveCategory<\/code>\u00a0to an empty string.<\/li>\n\n\n\n<li>This ensures the function only runs if all necessary information is available.<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li><strong>Direct Match Categorization<\/strong>:\n<ul class=\"wp-block-list\">\n<li>The function checks if the player\u2019s last move (<code>lastMove<\/code>) matches one of Stockfish\u2019s top three recommendations.<\/li>\n\n\n\n<li>If\u00a0<code>lastMove<\/code>\u00a0exactly matches\u00a0<code>previousTopMove<\/code>, it is categorized as \u201cTop,\u201d indicating the player made the optimal move.<\/li>\n\n\n\n<li>If\u00a0<code>lastMove<\/code>\u00a0matches either\u00a0<code>previousSecondMove<\/code>\u00a0or\u00a0<code>previousThirdMove<\/code>, it is categorized as a \u201cGood Move,\u201d meaning the player\u2019s move was strong but not the absolute best.<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li><strong>Evaluation Difference for Non-Matching Moves<\/strong>:\n<ul class=\"wp-block-list\">\n<li>If\u00a0<code>lastMove<\/code>\u00a0doesn\u2019t match any of Stockfish\u2019s top three moves, the function calculates an\u00a0<strong>evaluation difference<\/strong>. This difference is the absolute value between\u00a0<code>bestEvaluation<\/code>\u00a0(the score of Stockfish\u2019s best move) and\u00a0<code>previousTopLine.eval<\/code>\u00a0(the evaluation of the player\u2019s move).<\/li>\n\n\n\n<li>By measuring this difference, the function can quantify how much the player\u2019s move deviates from the optimal choice.<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li><strong>Threshold-Based Categorization<\/strong>:\n<ul class=\"wp-block-list\">\n<li>Based on the evaluation difference, the function assigns a category to the move:\n<ul class=\"wp-block-list\">\n<li><strong>Ok<\/strong>: If the difference is within 1 point, the move is close enough to the optimal line to be considered \u201cOk.\u201d<\/li>\n\n\n\n<li><strong>Inaccuracy<\/strong>: If the difference is within 2 points, the move is still reasonable but somewhat off from the best choice.<\/li>\n\n\n\n<li><strong>Blunder<\/strong>: If the difference exceeds 3 points, the move is significantly worse than the best option.<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<\/li>\n<\/ol>\n\n\n\n<p>In summary,&nbsp;<code>getMoveCategory<\/code>&nbsp;uses both direct comparisons and evaluation-based thresholds to classify moves, providing a clear assessment of the move quality based on Stockfish\u2019s analysis. This gives users immediate feedback on how their move stacks up against the optimal choices.<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p>Note: This is an example of how this function can be written, it does not currently take into account mating evaluations, but that is something you can look into.<\/p>\n<\/blockquote>\n\n\n\n<h3 class=\"wp-block-heading\"><a href=\"https:\/\/lichess.org\/@\/HollowLeaf\/blog\/chess-web-programming-part-five-game-review\/32JsEsrs#step-6-trigger-getmovecategory-in-useeffect\"><\/a>Step 6: Trigger&nbsp;<code>getMoveCategory<\/code>&nbsp;in&nbsp;<code>useEffect<\/code><\/h3>\n\n\n\n<p>We need&nbsp;<code>getMoveCategory<\/code>&nbsp;to run whenever&nbsp;<code>previousLines<\/code>,&nbsp;<code>bestEvaluation<\/code>, or&nbsp;<code>lastMove<\/code>&nbsp;changes. We\u2019ll use a&nbsp;<code>useEffect<\/code>&nbsp;hook to automatically call&nbsp;<code>getMoveCategory<\/code>&nbsp;when any of these dependencies update.<br>Add the following code:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>useEffect(() => {\n  if (previousLines.length > 0 &amp;&amp; bestEvaluation !== null &amp;&amp; lastMove) {\n    getMoveCategory();\n  }\n}, &#91;previousLines, bestEvaluation, lastMove, getMoveCategory]);<\/code><\/pre>\n\n\n\n<p>This ensures that each time a new move is made,&nbsp;<code>getMoveCategory<\/code>&nbsp;will determine its category based on Stockfish\u2019s evaluations.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><a href=\"https:\/\/lichess.org\/@\/HollowLeaf\/blog\/chess-web-programming-part-five-game-review\/32JsEsrs#step-7-display-the-move-category\"><\/a>Step 7: Display the Move Category<\/h3>\n\n\n\n<p>Finally, let\u2019s display the move category label on the page below the board.<br>Add the following JSX code within the return block:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>&lt;div>\n  &lt;h3>Move Category: {moveCategory}&lt;\/h3>\n&lt;\/div><\/code><\/pre>\n\n\n\n<p>This displays the category (e.g., &#8220;Top,&#8221; &#8220;Good Move,&#8221; or &#8220;Blunder&#8221;) each time a move is made and evaluated.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><a href=\"https:\/\/lichess.org\/@\/HollowLeaf\/blog\/chess-web-programming-part-five-game-review\/32JsEsrs#full-code-1\"><\/a>Full Code<\/h3>\n\n\n\n<p>After completing these steps, here\u2019s the full code:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>import React, { useState, useEffect, useCallback } from \"react\";\nimport { Chessboard } from \"react-chessboard\";\nimport { Chess } from \"chess.js\";\n\nconst App = () => {\n  \/\/ State to manage the chess game and Stockfish instance\n  const &#91;game, setGame] = useState(new Chess());\n  const &#91;stockfish, setStockfish] = useState(null);\n  const &#91;currentLines, setCurrentLines] = useState(&#91;]); \/\/ Holds evaluations for the current move\n  const &#91;previousLines, setPreviousLines] = useState(&#91;]); \/\/ Holds evaluations for the previous move\n  const &#91;bestEvaluation, setBestEvaluation] = useState(null); \/\/ Stores the eval value of the best move\n  const &#91;lastMove, setLastMove] = useState(null); \/\/ Stores the last move played by the user\n  const &#91;moveCategory, setMoveCategory] = useState(\"\"); \/\/ Stores the category of the user's move\n\n  \/\/ Load Stockfish Web Worker when the component mounts\n  useEffect(() => {\n    const stockfishInstance = new Worker(`${process.env.PUBLIC_URL}\/js\/stockfish-16.1-lite-single.js`);\n\n    setStockfish(stockfishInstance);\n\n    \/\/ Clean up by terminating the Stockfish instance when the component unmounts\n    return () => {\n      stockfishInstance.terminate();\n    };\n  }, &#91;]);\n\n  \/\/ Function to get top 3 evaluations and moves from Stockfish at a depth of 12\n  const getEvaluation = (fen) => {\n    if (!stockfish) return;\n\n    return new Promise((resolve) => {\n      const lines = &#91;]; \/\/ Array to store the top 3 lines of evaluations\n      stockfish.postMessage(\"setoption name MultiPV value 3\"); \/\/ Set Stockfish to calculate top 3 PVs\n      stockfish.postMessage(`position fen ${fen}`); \/\/ Set the position to the current FEN\n      stockfish.postMessage(\"go depth 12\"); \/\/ Instruct Stockfish to calculate up to a depth of 12\n\n      const isBlackTurn = fen.split(\" \")&#91;1] === \"b\"; \/\/ Check if it's Black's turn\n\n      \/\/ Handle messages from Stockfish\n      stockfish.onmessage = (event) => {\n        const message = event.data;\n\n        \/\/ Only process messages that contain evaluations at depth 12\n        if (message.startsWith(\"info depth 12\")) {\n          \/\/ Extract the evaluation score and principal variation (move sequence)\n          const match = message.match(\/score cp (-?\\d+).* pv (.+)\/);\n          if (match) {\n            let evalScore = parseInt(match&#91;1], 10) \/ 100; \/\/ Convert centipawn score to pawn units\n            const moves = match&#91;2].split(\" \"); \/\/ Split moves into an array\n\n            \/\/ Flip the evaluation score if it's Black's turn\n            if (isBlackTurn) {\n              evalScore = -evalScore;\n            }\n\n            \/\/ Add the evaluation and moves to the lines array\n            lines.push({ eval: evalScore, moves });\n\n            \/\/ Stop and resolve once we have the top 3 lines at depth 12\n            if (lines.length === 3) {\n              stockfish.postMessage(\"stop\"); \/\/ Stop Stockfish once we have 3 evaluations\n\n              \/\/ Sort lines based on whose turn it is\n              lines.sort((a, b) => (isBlackTurn ? a.eval - b.eval : b.eval - a.eval));\n\n              \/\/ Update previousLines with the current currentLines before refreshing currentLines\n              setPreviousLines(currentLines);\n              setCurrentLines(lines);\n\n              \/\/ Set bestEvaluation to the eval value of the top line for comparison\n              setBestEvaluation(lines&#91;0].eval);\n\n              resolve(lines); \/\/ Resolve the promise with the top 3 lines\n            }\n          }\n        }\n      };\n    });\n  };\n\n  \/\/ Function to determine and set the category of the last move based on evaluation\n  const getMoveCategory = useCallback(() => {\n    const previousTopLine = previousLines&#91;0];\n    const previousSecondLine = previousLines&#91;1];\n    const previousThirdLine = previousLines&#91;2];\n\n    \/\/ If any required data is missing, reset move category\n    if (!bestEvaluation || !lastMove || !previousTopLine) {\n      setMoveCategory(\"\");\n      return;\n    }\n\n    \/\/ Get the best moves from Stockfish's previous evaluations\n    const previousTopMove = previousTopLine?.moves&#91;0];\n    const previousSecondMove = previousSecondLine?.moves&#91;0];\n    const previousThirdMove = previousThirdLine?.moves&#91;0];\n\n    \/\/ Categorize move based on whether it matches the best moves or evaluation difference\n    if (lastMove === previousTopMove) {\n      setMoveCategory(\"Top\");\n    } else if (lastMove === previousSecondMove || lastMove === previousThirdMove) {\n      setMoveCategory(\"Good Move\");\n    } else {\n      const evaluationDifference = Math.abs(bestEvaluation - previousTopLine.eval);\n\n      \/\/ Set move category based on evaluation difference thresholds\n      if (evaluationDifference &lt;= 1) {\n        setMoveCategory(\"Ok\");\n      } else if (evaluationDifference &lt;= 2) {\n        setMoveCategory(\"Inaccuracy\");\n      } else if (evaluationDifference >= 3) {\n        setMoveCategory(\"Blunder\");\n      }\n    }\n  }, &#91;bestEvaluation, lastMove, previousLines]);\n\n  \/\/ Trigger getMoveCategory whenever evaluations or the last move change\n  useEffect(() => {\n    if (previousLines.length > 0 &amp;&amp; bestEvaluation !== null &amp;&amp; lastMove) {\n      getMoveCategory();\n    }\n  }, &#91;previousLines, bestEvaluation, lastMove, getMoveCategory]);\n\n  \/\/ Handle piece movement on the chessboard and trigger Stockfish evaluation\n  const onDrop = async (sourceSquare, targetSquare) => {\n    const gameCopy = new Chess(game.fen()); \/\/ Create a copy of the current game\n\n    try {\n      \/\/ Attempt to make the move on the game copy\n      const move = gameCopy.move({\n        from: sourceSquare,\n        to: targetSquare,\n        promotion: \"q\", \/\/ Automatically promote to a queen for simplicity\n      });\n\n      \/\/ If the move is invalid, exit the function\n      if (move === null) return false;\n\n      setGame(gameCopy); \/\/ Update the game state with the new move\n      setLastMove(`${sourceSquare}${targetSquare}`); \/\/ Save the last move played\n\n      \/\/ Get top 3 moves and evaluations from Stockfish at depth 12\n      await getEvaluation(gameCopy.fen());\n      return true;\n    } catch (error) {\n      console.error(error.message); \/\/ Log any errors during move attempt\n      return false;\n    }\n  };\n\n  return (\n    &lt;div>\n      &lt;h1>Game Review with Stockfish&lt;\/h1>\n      {\/* Chessboard component to display the game board *\/}\n      &lt;Chessboard position={game.fen()} onPieceDrop={onDrop} boardWidth={500} \/>\n\n      {\/* Display the top 3 evaluation lines *\/}\n      &lt;div>\n        &lt;h2>Top 3 Lines at Depth 12&lt;\/h2>\n        &lt;ul style={{ listStyleType: \"none\", paddingLeft: 0 }}>\n          {currentLines.map((line, index) => (\n            &lt;li key={index} style={{ marginBottom: \"10px\" }}>\n              &lt;strong>Line {index + 1}:&lt;\/strong> {line.eval} &lt;br \/>\n              &lt;strong>Moves:&lt;\/strong> {line.moves.join(\" \")}\n            &lt;\/li>\n          ))}\n        &lt;\/ul>\n      &lt;\/div>\n\n      {\/* Display the move category *\/}\n      &lt;div>\n        &lt;h3>Move Category: {moveCategory}&lt;\/h3>\n      &lt;\/div>\n    &lt;\/div>\n  );\n};\n\nexport default App;<\/code><\/pre>\n\n\n\n<p>You should now see the following:<br><img decoding=\"async\" src=\"https:\/\/image.lichess1.org\/display?fmt=webp&amp;h=0&amp;op=resize&amp;path=hollowleaf:ublogBody:6boYcOvxVxKh:BT3QTVVd.png&amp;w=800&amp;sig=82b1f128c99fd5c3af49f79ba6f5b9c216be18e9\" alt=\"image.png\"><\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><a href=\"https:\/\/lichess.org\/@\/HollowLeaf\/blog\/chess-web-programming-part-five-game-review\/32JsEsrs#summary\"><\/a>Summary<\/h2>\n\n\n\n<p>In this project, we\u2019ve developed an interactive chessboard application that not only allows users to play moves but also evaluates each move in real-time using Stockfish, a powerful chess engine. Our application provides insights into each move by categorizing it based on how closely it aligns with the engine\u2019s top recommendations, labeling moves as \u201cTop,\u201d \u201cGood Move,\u201d \u201cOk,\u201d \u201cInaccuracy,\u201d or \u201cBlunder.\u201d This categorization allows users to understand the quality of each move, helping them improve their play and learn from Stockfish&#8217;s analysis.<br>This project is a basic example of a game review tool, designed to showcase what can be achieved with a chess engine like Stockfish integrated into a web application. There\u2019s plenty of potential for further development: we could fine-tune the move categorization, implement logic to identify theoretical or &#8220;book&#8221; moves, display visual representations of suggested moves on the board, or add customization options. With these enhancements, the application could offer a more comprehensive and engaging experience for users looking to analyze and learn from their games.<br>I hope you have enjoyed this five-part series on building a Chess Web Application. Feel free to reach out with any questions or leave a comment\u2014I\u2019d love to hear your thoughts!<\/p>\n\n\n\n<h4 class=\"wp-block-heading\"><a href=\"https:\/\/lichess.org\/@\/HollowLeaf\/blog\/chess-web-programming-part-five-game-review\/32JsEsrs#learn-more\"><\/a><strong>Learn more<\/strong><\/h4>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"https:\/\/reactjs.org\/\">React<\/a>\u00a0\u2013 A JavaScript library for building user interfaces.<\/li>\n\n\n\n<li><a href=\"https:\/\/www.npmjs.com\/package\/react-chessboard\">react-chessboard<\/a>\u00a0\u2013 A React component for rendering a chessboard.<\/li>\n\n\n\n<li><a href=\"https:\/\/www.npmjs.com\/package\/chess.js\">chess.js<\/a>\u00a0\u2013 A library for handling chess game rules and move validation.<\/li>\n\n\n\n<li><a href=\"https:\/\/stockfishchess.org\/\">Stockfish<\/a>\u00a0\u2013 A powerful open-source chess engine.<\/li>\n\n\n\n<li><a href=\"https:\/\/github.com\/nmrugg\/stockfish.js\">stockfish.js<\/a>\u00a0\u2013 A JavaScript and WebAssembly version of Stockfish for web applications.<\/li>\n\n\n\n<li><a href=\"https:\/\/www.w3schools.com\/css\/\">CSS<\/a>\u00a0\u2013 A styling language used to design and customize HTML elements.<\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>Creating a Game Review Application Building chess web applications has been a fun and challenging journey, combining the strategies of chess with the world of web programming. Since starting this project, I\u2019ve watched&nbsp;Chessboard Magic&nbsp;grow from a simple app into a platform with 34 unique features that make learning and playing chess more interactive and enjoyable.In [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":39,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[],"class_list":["post-38","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-uncategorized"],"_links":{"self":[{"href":"https:\/\/blog.chessboardmagic.com\/index.php\/wp-json\/wp\/v2\/posts\/38","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/blog.chessboardmagic.com\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blog.chessboardmagic.com\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blog.chessboardmagic.com\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.chessboardmagic.com\/index.php\/wp-json\/wp\/v2\/comments?post=38"}],"version-history":[{"count":1,"href":"https:\/\/blog.chessboardmagic.com\/index.php\/wp-json\/wp\/v2\/posts\/38\/revisions"}],"predecessor-version":[{"id":40,"href":"https:\/\/blog.chessboardmagic.com\/index.php\/wp-json\/wp\/v2\/posts\/38\/revisions\/40"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/blog.chessboardmagic.com\/index.php\/wp-json\/wp\/v2\/media\/39"}],"wp:attachment":[{"href":"https:\/\/blog.chessboardmagic.com\/index.php\/wp-json\/wp\/v2\/media?parent=38"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.chessboardmagic.com\/index.php\/wp-json\/wp\/v2\/categories?post=38"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.chessboardmagic.com\/index.php\/wp-json\/wp\/v2\/tags?post=38"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}