{"id":141,"date":"2021-08-04T16:33:26","date_gmt":"2021-08-04T16:33:26","guid":{"rendered":"http:\/\/archive.gagvani.com\/?p=141"},"modified":"2021-10-24T00:26:20","modified_gmt":"2021-10-24T00:26:20","slug":"how-i-built-a-complete-nba-game-simulator-with-less-than-500-lines-of-code","status":"publish","type":"post","link":"https:\/\/archive.gagvani.com\/index.php\/2021\/08\/04\/how-i-built-a-complete-nba-game-simulator-with-less-than-500-lines-of-code\/","title":{"rendered":"How I built a complete NBA game simulator with less than 500 lines of code"},"content":{"rendered":"\n<h2 class=\"wp-block-heading\">Introduction<\/h2>\n\n\n\n<p>Basketball is one of my primary interests. I&#8217;ve always been attracted to not only the <a href=\"https:\/\/www.google.com\/url?sa=t&amp;rct=j&amp;q=&amp;esrc=s&amp;source=video&amp;cd=&amp;cad=rja&amp;uact=8&amp;ved=2ahUKEwjsup_9gPfxAhWEGs0KHQkqCP0QtwIwAXoECAgQAw&amp;url=https%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3DxxRQ5nne7fs&amp;usg=AOvVaw2GrkNEUmEwe52Qpnn6S3pN\" target=\"_blank\" rel=\"noreferrer noopener\" title=\"https:\/\/www.google.com\/url?sa=t&amp;rct=j&amp;q=&amp;esrc=s&amp;source=video&amp;cd=&amp;cad=rja&amp;uact=8&amp;ved=2ahUKEwjsup_9gPfxAhWEGs0KHQkqCP0QtwIwAXoECAgQAw&amp;url=https%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3DxxRQ5nne7fs&amp;usg=AOvVaw2GrkNEUmEwe52Qpnn6S3pN\">highlight plays<\/a>, but also the wealth of interesting statistical tidbits out there. Sites like <a href=\"https:\/\/fivethirtyeight.com\/tag\/nba\/\" target=\"_blank\" rel=\"noreferrer noopener\" title=\"https:\/\/fivethirtyeight.com\/tag\/nba\/\">FiveThirtyEight<\/a> and others do a great job of digging up advanced stats to prove a point. After creating a program to automatically pull NBA stats from <a href=\"http:\/\/stats.nba.com\" target=\"_blank\" rel=\"noreferrer noopener\" title=\"stats.nba.com\">stats.nba.com<\/a>, I decided to create an entire app for creating virtual NBA teams, and pitting them against each other. Here&#8217;s how I did it. <\/p>\n\n\n\n<h2 class=\"wp-block-heading\">The Approach<\/h2>\n\n\n\n<p>Clearly, such a complex project couldn&#8217;t be coded in a simple script. I would need to really embrace <a href=\"https:\/\/realpython.com\/python3-object-oriented-programming\/\" target=\"_blank\" rel=\"noreferrer noopener\" title=\"https:\/\/realpython.com\/python3-object-oriented-programming\/\">OOP<\/a>(Object-Oriented Programming) for this. I decided to create 4 main classes: the <strong>Player<\/strong>, <strong>Team<\/strong>, <strong>Game<\/strong>, and <strong>GUI<\/strong>. <\/p>\n\n\n\n<ul><li><strong>Players <\/strong>store information about their shooting percentages(how often they make shots), and can also simulate &#8220;shooting the ball&#8221;. <\/li><li><strong>Teams <\/strong>contain 5 players, as well as the current score for that team. <\/li><li><strong>Game <\/strong>contains 2 teams, as well as logic for the actual game simulation.<\/li><li>The <strong>GUI <\/strong>displays a list of players and allows the user to create virtual teams, for a simulated match. <\/li><\/ul>\n\n\n\n<p>Finally, the main loop will read the data for each player, and then start the GUI. <\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Coding the <strong>Player<\/strong><\/h2>\n\n\n\n<figure class=\"wp-block-image size-large is-resized\"><img decoding=\"async\" src=\"https:\/\/static01.nyt.com\/images\/2020\/01\/13\/sports\/13nba-kyrieWEB1\/merlin_167052225_82cee84c-d59e-45f2-80c6-2b7cbaaa6461-superJumbo.jpg\" alt=\"\" width=\"504\" height=\"335\"\/><\/figure>\n\n\n\n<p>I had a lot of really fine-grained data at my disposal. Namely, the shooting percentages, number of shot attempts, and number of made shots from 5 different zones on the court. To begin, I created a Player class, and put this data in the constructor:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: python; first-line: 11; title: ; notranslate\" title=\"\">\n#Class defining the parameters of the player\nclass Player:\n    def __init__(self, name,RestrictArea_FGpercent,Paint_FGpercent,MidRange_FGpercent,LeftCorner3_FGpercent,RightCorner3_FGpercent,AboveBreak3_FGpercent,\\\n                 RestrictArea_FGA,Paint_FGA,MidRange_FGA,LeftCorner3_FGA,RightCorner3_FGA,Totals_FGA,AboveBreak3_FGA,\\\n                 RestrictArea_FGM,Paint_FGM,MidRange_FGM,LeftCorner3_FGM,RightCorner3_FGM,AboveBreak3_FGM,Totals_FGM):\n<\/pre><\/div>\n\n\n<p>Here, I added all the data available for each player into the constructor. Now, I needed to make all this data attributes of the Player class, so they can be used later. <\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: python; first-line: 16; title: ; notranslate\" title=\"\">\n        self.name = name.strip()   # Remove any whitespace\n        self.RestrictArea_FGpercent = float(RestrictArea_FGpercent)\n        self.Paint_FGpercent = float(Paint_FGpercent)\n        self.MidRange_FGpercent = float(MidRange_FGpercent)\n        self.LeftCorner3_FGpercent = float(LeftCorner3_FGpercent)\n        self.RightCorner3_FGpercent = float(RightCorner3_FGpercent)\n        self.AboveBreak3_FGpercent = float(AboveBreak3_FGpercent)\n\n        self.RestrictArea_FGA = float(RestrictArea_FGA)\n        self.Paint_FGA = float(Paint_FGA)\n        self.MidRange_FGA = float(MidRange_FGA)\n        self.LeftCorner3_FGA = float(LeftCorner3_FGA)\n        self.RightCorner3_FGA = float(RightCorner3_FGA)\n        self.Totals_FGA = float(Totals_FGA)\n        self.AboveBreak3_FGA = float(AboveBreak3_FGA)\n\n        self.RestrictArea_FGM = float(RestrictArea_FGM)\n        self.Paint_FGM = float(Paint_FGM)\n        self.MidRange_FGM = float(MidRange_FGM)\n        self.LeftCorner3_FGM = float(LeftCorner3_FGM)\n        self.RightCorner3_FGM = float(RightCorner3_FGM)\n        self.AboveBreak3_FGM = float(AboveBreak3_FGM)\n        self.Totals_FGM = float(Totals_FGM)  \n<\/pre><\/div>\n\n\n<p>The <code>self.<\/code> in each of these lines is saying that each of these attributes is an attribute of an <em>instance <\/em>of that class, rather than the class in general. This allows each Player to have different shooting percentages. The reason why each parameter has to be converted into a float is because they are read as strings, but Python can&#8217;t do math on those. <\/p>\n\n\n\n<p>In order to simplify the rest of the code, I decided to make a player do two very simple things. Shoot a two-pointer, or shoot a three-pointer. Thus, I need to combine all these fine-grained data points into the attempts, made shots, and percentage for two-pointers and three-pointers. <\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: python; first-line: 39; title: ; notranslate\" title=\"\">\n        self.total2PA=self.RestrictArea_FGA+self.Paint_FGA+self.MidRange_FGA\n        self.total3PA=self.LeftCorner3_FGA+self.RightCorner3_FGA+self.AboveBreak3_FGA\n<\/pre><\/div>\n\n\n<p>Above, I calculated the total 2-point attempts and 3-point attempts by adding up the attempts from each zone which it belongs to. <\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: python; first-line: 41; title: ; notranslate\" title=\"\">\n        if self.total2PA == 0: # prevent division by zero\n            self.TwoPTpercent=0\n        else:\n            self.TwoPTpercent = (self.RestrictArea_FGM+self.Paint_FGM+self.MidRange_FGM)\/(self.total2PA)\n        if self.total3PA == 0:\n            self.ThreePTpercent=0\n        else:   \n            self.ThreePTpercent = (self.LeftCorner3_FGM+self.RightCorner3_FGM+self.AboveBreak3_FGM)\/(self.total3PA)\n\n<\/pre><\/div>\n\n\n<p>Now, I calculated the 2-point and 3-point shooting percentages by dividing the amount of shots made by the amount of shots attempted in each zone. If no shots were attempted by a player in a certain zone (this usually happens with three-pointers), the shooting percentage automatically equals zero. <\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: python; first-line: 50; title: ; notranslate\" title=\"\">\n        self.pointsScored = 0\n        self.twoPTM = 0\n        self.twoPTA = 0\n        self.threePTM = 0\n        self.threePTA = 0\n<\/pre><\/div>\n\n\n<p>Here, I&#8217;ve initialized the attributes which will be updated, and then reset during and after every <strong>simulated <\/strong>game. (M refers to shots made, and A refers to shots attempted. So <code>self.threePTM<\/code>, for example, is referring to the amount of three pointers the player made during a simulated game.)<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: python; first-line: 55; title: ; notranslate\" title=\"\">\n        self.fga=self.RestrictArea_FGA+self.Paint_FGA+self.MidRange_FGA+self.LeftCorner3_FGA+self.RightCorner3_FGA+self.AboveBreak3_FGA\n<\/pre><\/div>\n\n\n<p>Here I calculated the total amount of shots a player takes per game. This will be useful down the line when we have to allocate shot attempts to different players. <\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: python; first-line: 56; title: ; notranslate\" title=\"\">\n        self.lotsOfShots2=random.choices(&#x5B;1,0],weights=&#x5B;self.TwoPTpercent,1-self.TwoPTpercent],k=300)\n        self.lotsOfShots3=random.choices(&#x5B;1,0],weights=&#x5B;self.ThreePTpercent,1-self.ThreePTpercent],k=300)\n<\/pre><\/div>\n\n\n<p>There&#8217;s a lot to unpack here. Let&#8217;s start on the outside, and go in. <\/p>\n\n\n\n<p><a href=\"https:\/\/docs.python.org\/3\/library\/random.html#random.choices\" target=\"_blank\" rel=\"noreferrer noopener\" title=\"https:\/\/docs.python.org\/3\/library\/random.html#random.choices\">random.choices<\/a>, according to the documentation, &#8220;Return(s) a <em>k<\/em> sized list of elements chosen from the <em>population<\/em> with replacement. If the <em>population<\/em> is empty, raises <a href=\"https:\/\/docs.python.org\/3\/library\/exceptions.html#IndexError\"><code>IndexError<\/code><\/a>.&#8221; Essentially, it&#8217;s choosing something from a list with certain weights <em>k <\/em>times. <\/p>\n\n\n\n<p>What is being chosen here. Either a 1 or a 0, which we can see in the list <code>[1,0]<\/code>. A 1 corresponds with a made shot, and a 0 is a missed shot. Now, what would the weights for a made or missed shot be? Clearly, they should be the player&#8217;s shooting percentages, which they are. The weight for a 1, or made shot is equal to the 2 or 3-point percentage, while the probability of missing is 1 minus that(a simple probability rule). <\/p>\n\n\n\n<p><em>What&#8217;s actually happening here? <\/em>300 shot attempts, from both 2-point and 3-point zones, have been simulated. Why 300? Realistically, no one player is going to shoot more than 30-40 times in a game. Normally, both <em>teams <\/em>combine for around 200 shot attempts. These simulated shot attempts will be used later, when the game is being &#8220;played&#8221;.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: python; first-line: 59; title: ; notranslate\" title=\"\">\n    def reset_score(self):\n        self.pointsScored=0\n\n    def get_score(self):\n        return self.pointsScored\n\n    def get_splits(self):\n        return(self.twoPTM,self.twoPTA,self.threePTM,self.threePTA)\n\n    def reset_splits(self):\n        self.twoPTM = 0\n        self.twoPTA = 0\n        self.threePTM = 0\n        self.threePTA = 0\n<\/pre><\/div>\n\n\n<p>These are some helpful functions that act as <em>getters <\/em>and <em>setters<\/em>. In object-oriented programming, you don&#8217;t want a class to expose its attributes. Instead, it&#8217;s a good idea to write functions which encapsulate this action. The function <code>reset_score()<\/code> changes the Player&#8217;s <code>pointsScored<\/code> attribute back to zero. <code>get_score()<\/code> returns the attribute <code>pointsScored<\/code>. In Python, it&#8217;s not possible to declare a variable private, but in another language it should be private. The functions get_splits() and reset_splits() are a getter and setter for the player&#8217;s simulated game stats. <\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: python; first-line: 74; title: ; notranslate\" title=\"\">\n    def shoot2pt(self,turn):\n        if self.lotsOfShots2&#x5B;turn] == 1: #Selecting one shot out of the lots of shots\n            return True  #Make shot\n        else:\n            return False\n    def shoot3pt(self,turn):\n        if self.lotsOfShots3&#x5B;turn] == 1: #Selecting one shot out of the lots of shots\n            return True  #Make shot\n        else:\n            return False\n<\/pre><\/div>\n\n\n<p>The functions <code>shoot2pt() <\/code>and <code>shoot3pt()<\/code> are essentially wrapping the data that is stored in <code>lotsOfShots2 <\/code>and <code>lotsOfShots3<\/code>. They take the current turn(possession number, in basketball terms) and figure out whether the player would have made the shot(1, so return <code>True<\/code>), or not made the shot(0, so return <code>False<\/code>). Since we don&#8217;t want the actual game simulation to access the <code>lotsOfShots <\/code>attributes directly, this function is needed to act as a getter function.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Coding the Team<\/h2>\n\n\n\n<figure class=\"wp-block-image size-large is-resized\"><img decoding=\"async\" src=\"https:\/\/nothinbutnets.com\/wp-content\/uploads\/getty-images\/2021\/02\/1297525742.jpeg\" alt=\"\" width=\"564\" height=\"375\"\/><\/figure>\n\n\n\n<p>Compared to the Player, which had a lot of complex logic and arguments being passed into it, there isn&#8217;t much going on in a Team. It&#8217;s just wrapper functions for managing players, and keeping track of its score. <\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: python; first-line: 90; highlight: [90]; title: ; notranslate\" title=\"\">\nclass Team:\n    def __init__(self,name,pct2PA,pct3PA): #team name, %of shots 2pt, %of shots 3pt, possessions per game\n        self.name = name\n        self.players = &#x5B;]\n        self.pct2PA = pct2PA\n        self.pct3PA = pct3PA\n<\/pre><\/div>\n\n\n<p>First, I created the Team class, and passed the team name, percentage of shots that are 2-pointers percentage of shots that are three-pointers, and possessions per game in the constructor. (Note: the variables pct2PA and pct3PA are not actually being used. They are for future expansion). <\/p>\n\n\n\n<p>In the constructor the name is passed in and becomes an attribute, as well as the 2-pointer and 3-pointer shooting splits. An empty list is also initialized, which will store the team&#8217;s players. <\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: python; first-line: 96; title: ; notranslate\" title=\"\">\n    def addPlayer(self,player):\n        self.players.append(player) #should be a player object\n    def reset_scores(self):\n        for player in self.players:\n            player.reset_score()\n    def get_scores(self):\n        scores = &#x5B;]\n        for player in self.players:\n            scores.append(player.get_score())\n        return scores\n    def get_shooting_splits(self):\n        splits = &#x5B;]\n        for player in self.players:\n            splits.append(player.get_splits())\n        return splits\n    def reset_splits(self):\n        for player in self.players:\n            player.reset_splits()\n<\/pre><\/div>\n\n\n<p>This is the rest of the Team class. <\/p>\n\n\n\n<ul id=\"teamfuncs\"><li>The function <code>addPlayer()<\/code> takes a Player object and appends it to the list of players which the Team stores.<\/li><li>The functions <code>reset_scores()<\/code> and <code>get_scores()<\/code> are very representative of Object-Oriented structure. Instead of the Game or GUI class calling methods of Player, they instead call a function from the class right under it. So, the Game calls function from Team, and the Team calls functions from Player. <\/li><li>This is very evident, especially in the last two functions, where each player in the team is iterated over. <\/li><\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Creating the Game logic<\/h2>\n\n\n\n<figure class=\"wp-block-image size-large is-resized\"><img decoding=\"async\" src=\"https:\/\/www.ctvnews.ca\/polopoly_fs\/1.5052524.1596660348!\/httpImage\/image.jpg_gen\/derivatives\/landscape_1020\/image.jpg\" alt=\"picture of grizzlies vs jazz game in the nba bubble\" width=\"718\" height=\"403\"\/><\/figure>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: python; first-line: 372; title: ; notranslate\" title=\"\">\n#game logic\nclass Game:\n    def __init__(self,teams,players,numPossessions): #players is the list of ALL PLAYER OBJECTS. \n        self.team1=teams&#x5B;0]\n        self.team2=teams&#x5B;1]\n        self.teams=teams\n        \n        self.team1Score=0\n        self.team2Score=0\n\n        self.players=players\n        self.numPossessions=numPossessions\n        self.turnsPlayed=0\n\n        self.fgaOrder=&#x5B;]\n\n<\/pre><\/div>\n\n\n<p>A Game takes in 2 Team objects in a list, a list of <em>all player objects that can exist<\/em>, and the number of possessions(every time a team gets the ball and attempts to score) as parameters. In the constructor, each team is assigned to team1 or team2, and each team&#8217;s score is also set to zero.<\/p>\n\n\n\n<p>Furthermore, the list of all players, and the number of possessions are both made into attributes of the Game. An empty list named <code>fgaOrder <\/code>is also initialized. What is it? It will be used to determine <strong>which player<\/strong> will <strong>shoot the ball<\/strong> on <strong>each turn<\/strong>.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: python; first-line: 388; title: ; notranslate\" title=\"\">\n    def add_players_to_teams(self,team1players,team2players):\n\n        for playerName in team1players:\n            for playerObj in self.players:\n                if playerObj.name == playerName:\n                    self.team1.addPlayer(playerObj)\n                    break\n\n        for playerName in team2players:\n            for playerObj in self.players:\n                if playerObj.name == playerName:\n                    self.team2.addPlayer(playerObj)\n                    break\n<\/pre><\/div>\n\n\n<p>The first function in Game is <code>add_players_to_teams()<\/code>. Of course, there can&#8217;t be a game without players on each team. This function takes two lists of <em>player names<\/em> as parameters(not to be confused with Player objects). Then, in each of the for-loops, a list of <em>all <\/em>Player objects is iterated through, and if there is a match between the given player name and the Player object, then that Player object is added to team1 or team2. (This does make it possible for there to be the same player on two teams. However, in this case the instance of the Player is the same in both lists so that player&#8217;s score counts for both teams.)<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: python; first-line: 402; title: ; notranslate\" title=\"\">\n        #Once teams are know figure out distribution of possessions for players\n        playerFGAs={} # &lt;name&gt; : &lt;fga&gt;\n        allAttempts=&#x5B;]\n        \n        allPlayerNames=team1players+team2players\n        totalFGA=0 #FOR BOTH TEAMS\n        for playerName in allPlayerNames:\n            for player in self.team1.players:\n                if player.name == playerName:\n                    playerFGAs&#x5B;player.name] = player.fga\n                    totalFGA+=player.fga\n\n            for player in self.team2.players:\n                if player.name == playerName:\n                    playerFGAs&#x5B;player.name] = player.fga\n                    totalFGA+=player.fga\n<\/pre><\/div>\n\n\n<p>Now that we have our teams, we can dole out shot attempts to all the players who are playing. The dictionary playerFGAs stores a player&#8217;s name as the key and that player&#8217;s average shot attempts as the value. It will be used for determining how many shot attempts each player should get, relative to one another. Furthermore, totalFGA is used to count the number of shot attempts that would happen, if you merely added up the attempts for each player. The for-loops iterate over each team&#8217;s players to find these values. <\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: python; first-line: 419; title: ; notranslate\" title=\"\">\n        normFactor=self.numPossessions\/totalFGA\n<\/pre><\/div>\n\n\n<p>Now that we know the theoretical number of shot attempts, we can normalize this to the number of shot attempts we want the game to actually have. <\/p>\n\n\n\n<p>Basically, if you had a team of five Michael Jordans, in a vacuum they would take far too many shots for a normal game. In this case, the normFactor would be between 0 and 1 because each Michael Jordan has to take less shots. On the other hand, if you had a team of five benchwarmers, who do not get a lot of playing time, the normalization factor for their shot attempts would be greater than 1, since they would normally not take enough shots. <\/p>\n\n\n\n<p>This is a phenomenon found a lot in real NBA basketball. It&#8217;s fairly common to see a team&#8217;s #2 option step up their game in the absence of their team&#8217;s alpha player.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: python; first-line: 421; title: ; notranslate\" title=\"\">\n        for playerName in allPlayerNames:\n            normFGA=int(round(normFactor*playerFGAs&#x5B;playerName]))\n            playerFGAs&#x5B;playerName]=normFGA #filling out the dict: Now it is normalized\n\n        for playerName in allPlayerNames:\n            for attemptNumber in range(1,playerFGAs&#x5B;playerName]):\n                allAttempts.append(playerName) \n<\/pre><\/div>\n\n\n<p>Now that we have calculated the normalization factor for shot attempts, we first complete the normalization of the shot attempts in the first for-loop. <\/p>\n\n\n\n<p>In the second section, the outer for-loop iterates through the name of each player playing in the game. The inner for-loop appends a player&#8217;s name the amount of times they are supposed to have the ball. For example, let&#8217;s just say there are 2 players right now, LeBron James and James Harden. The playerFGAs dictionary reads:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: python; gutter: false; title: ; notranslate\" title=\"\">\n{&quot;LeBron James&quot;:3, &quot;James Harden&quot;:4}\n<\/pre><\/div>\n\n\n<p>Now, allAttempts, which is basically of who would shoot on each possession, looks like this:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: python; gutter: false; title: ; notranslate\" title=\"\">\n&#x5B;&quot;LeBron James&quot;, &quot;LeBron James&quot;, &quot;LeBron James&quot;, &quot;James Harden&quot;, &quot;James Harden&quot;, &quot;James Harden&quot;, &quot;James Harden&quot;]\n<\/pre><\/div>\n\n\n<p>According to this, LeBron would take the first three shots, and James Harden would take the last four. But that&#8217;s clearly not how it usually works. In order to make this more realistic, we need to shuffle the list:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: python; gutter: false; title: ; notranslate\" title=\"\">\n        self.fgaOrder=random.sample(allAttempts,len(allAttempts))\n<\/pre><\/div>\n\n\n<p>Wait a minute! This isn&#8217;t shuffling! It&#8217;s sampling! But, with a closer look, you can see that the sample is the same size as the original list. Thus, this random sample is basically the same thing as shuffling: It randomly picks players until there are none left.<\/p>\n\n\n\n<p>There&#8217;s one more edge case we have to consider, though: What if the number of possessions in the game is longer than the fgaOrder we have already determined? <\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: python; first-line: 431; title: ; notranslate\" title=\"\">\n        # if numPossession is more than length of fgaOrder,  add at the end\n        for i in range(1 + self.numPossessions - len(self.fgaOrder)):\n               randomPlayerName = random.choice(allPlayerNames)\n               self.fgaOrder.append(randomPlayerName)\n<\/pre><\/div>\n\n\n<p>Here we loop the number of times that fgaOrder has to be appended to. Since a for-loop stops 1 before the end of the range we need to add 1. Then, we add a random player to the end of <code>fgaOrder <\/code>until there are enough field goal attempts. <\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: python; first-line: 436; title: ; notranslate\" title=\"\">\n    def init_game(self):\n        self.team1Score=0\n        self.team2Score=0\n        self.turnsPlayed=0\n        self.team1.reset_scores()\n        self.team2.reset_scores()\n        self.team1.reset_splits()\n        self.team2.reset_splits()\n        self.team1.players = &#x5B;]\n        self.team2.players = &#x5B;]\n<\/pre><\/div>\n\n\n<p>The function <code>init_game()<\/code> simply resets the scores and shooting numbers, and empties each teams&#8217; <code>players <\/code>list so it can be re-populated with 2 new sets of Players. <\/p>\n\n\n\n<p>Next, we move on to the real essence of the Game, which is the logic for playing a turn(also referred to as a possession here, and in basketball lingo):<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: python; first-line: 448; title: ; notranslate\" title=\"\">\n    def play_turn(self):\n        if self.turnsPlayed &gt;= self.numPossessions:\n            return False #ERROR\n<\/pre><\/div>\n\n\n<p>The first thing that we do in <code>play_turn()<\/code> is check if we have already played enough turns. If we have, then there is no reason to continue and <code>play_turn() <\/code>will return <code>False<\/code>, signaling an error.  <\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: python; first-line: 452; title: ; notranslate\" title=\"\">\n        playerNameWithBall = self.fgaOrder&#x5B;self.turnsPlayed]\n        for player in self.team1.players + self.team2.players:\n            if player.name == playerNameWithBall:\n                playerWithBall = player\n                break\n<\/pre><\/div>\n\n\n<p>Now, we first need to figure out which player has the ball. Thus, we first look up the list <code>fgaOrder <\/code>for the current turn, in order to find out the name of the player who is supposed to shoot. Then, we iterate over each player which is playing in the game. If the names match, we can set a variable, <code>playerWithBall<\/code>, which is the corresponding Player object.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: python; first-line: 459; title: ; notranslate\" title=\"\">\n       turnoutcome = f&quot;{playerWithBall.name} did not score&quot;\n<\/pre><\/div>\n\n\n<p>The string turnoutcome is the message the user will see in the GUI. By default, we assume that the player with the ball does not score. However, if the player does make a basket this string will be updated. <\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: python; first-line: 461; title: ; notranslate\" title=\"\">\n        position=random.choice((&quot;3&quot;,&quot;2&quot;)) #Goes to 3pt line or 2pt\n<\/pre><\/div>\n\n\n<p>Here, the player is randomly either going to shoot a 3-pointer or a 2-pointer. Right now, there is a 50\/50 chance of getting either, which is reasonably close to how the NBA really operates, however, this can be changed to be a weighted choice. <\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: python; first-line: 463; title: ; notranslate\" title=\"\">\n        if position == &quot;3&quot;:  #if they are going to shoot a 3\n            make=playerWithBall.shoot3pt(self.turnsPlayed)\n            if make == True: #true is make\n                #print(f&quot;{playerWithBall.name} made a 3&quot;)\n                turnoutcome = f&quot;{playerWithBall.name} made a 3&quot;\n                if playerWithBall in self.team1.players: \n                    self.team1Score +=3\n                    playerWithBall.pointsScored+=3\n                    playerWithBall.threePTM += 1\n                    playerWithBall.threePTA += 1\n                    playerWithBall=random.choice(self.team2.players) #Inbound ball to person on other team\n                elif playerWithBall in self.team2.players:\n                    self.team2Score +=3\n                    playerWithBall.pointsScored+=3\n                    playerWithBall.threePTM += 1\n                    playerWithBall.threePTA += 1\n                    playerWithBall=random.choice(self.team1.players)\n                else:\n                    #print(&quot;Apparently the ball went to the ref or something.&quot;)\n                    pass\n            elif make == False:  #Miss shot\n                playerWithBall.threePTA += 1\n                playerWithBall=random.choice(self.team1.players+self.team2.players)\n<\/pre><\/div>\n\n\n<p>Now, this section essentially is simulating the process of shooting a <strong>3-pointer. <\/strong>First, the variable &#8220;make&#8221;, which is boolean, determines whether the player makes the shot or not. Remember, the function shoot3pt() uses the precomputed list <code>lotsOfShots <\/code>to find whether or not a player would make the shot on the given turn number. If the  Player did make the shot, the turnoutcome string is updated to reflect that. Then, the appropriate team score, player score and shooting splits are updated. Then, since the player made a shot, the ball should be given to a player on the other team to inbound. However, if the player misses, then that player records a three point field goal attempt. The ball is rebounded by any player on the court. However, in practice, there is a higher chance that it will go to the defensive team. (See offensive and defensive rebounding % for advanced statistics on this). <\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: python; first-line: 488; title: ; notranslate\" title=\"\">\n        if position == &quot;2&quot;:  #if they are going to shoot a 2\n            make=playerWithBall.shoot2pt(self.turnsPlayed)\n            if make == True: #true is make\n                #print(f&quot;{playerWithBall.name} made a 2&quot;)\n                turnoutcome = f&quot;{playerWithBall.name} made a 2&quot;\n                if playerWithBall in self.team1.players: \n                    self.team1Score +=2\n                    playerWithBall.pointsScored+=2\n                    playerWithBall.twoPTM += 1\n                    playerWithBall.twoPTA += 1\n                    playerWithBall=random.choice(self.team2.players)\n                elif playerWithBall in self.team2.players:\n                    self.team2Score +=2\n                    playerWithBall.pointsScored+=2\n                    playerWithBall.twoPTM += 1\n                    playerWithBall.twoPTA += 1\n                    playerWithBall=random.choice(self.team1.players)\n                else:\n                    #print(&quot;Apparently the ball went to the ref or something.&quot;)\n                    pass\n            elif make == False:\n                playerWithBall.twoPTA += 1\n                playerWithBall=random.choice(self.team1.players+self.team2.players)\n<\/pre><\/div>\n\n\n<p>The exact same process is repeated if the player with the ball shoots a 2-pointer. (You may have noticed we crossed 500 lines of code in this code snippet. That&#8217;s because the line numbers include comments, old code, and whitespace. In reality there are 498 lines of actually functioning code).<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: python; first-line: 512; title: ; notranslate\" title=\"\">\n        self.turnsPlayed+=1 \n\n        # print(self.team1Score,self.team2Score,turnoutcome)\n        return &#x5B;self.team1Score,self.team2Score,turnoutcome]\n<\/pre><\/div>\n\n\n<p>Finally, at the end of <code>play_turn()<\/code>, we increment the amount of turns played by one, and return each team&#8217;s score along with the turn&#8217;s outcome. <\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: python; first-line: 530; title: ; notranslate\" title=\"\">\n    def get_team1_scores(self):\n        return self.team1.get_scores()\n\n    def get_team2_scores(self):\n        return self.team2.get_scores()\n\n    def get_team1_splits(self):\n        return self.team1.get_shooting_splits()\n\n    def get_team2_splits(self):\n        return self.team2.get_shooting_splits()\n<\/pre><\/div>\n\n\n<p>To wrap up the Game class, we have some getter functions that will be used in conjunction with the GUI class. <\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: python; first-line: 542; title: ; notranslate\" title=\"\">\n    def del_from_team1(self,playerName):\n        idx= 0\n        for player in self.team1.players:\n            if player.name == playerName:\n               print(&quot;In Team 1 Delete for player &quot; + playerName)\n               del self.team1.players&#x5B;idx]\n               break\n            idx+=1\n\n    def del_from_team2(self,playerName):\n        idx= 0\n        for player in self.team2.players:\n            if player.name == playerName:\n                print(&quot;In Team2 Delete for player &quot; + playerName)\n                del self.team2.players&#x5B;idx]\n                break\n            idx+=1\n<\/pre><\/div>\n\n\n<p>Finally, both of these setter functions deletes a <code>Player <\/code>from a <code>Team<\/code>, given a string which is the player&#8217;s name. It does this by iterating through the <code>Players <\/code>until the one to be deleted is found. And that&#8217;s it! Our Game class is complete!<\/p>\n\n\n\n<figure class=\"wp-block-image size-large is-resized\"><img decoding=\"async\" src=\"https:\/\/archive.gagvani.com\/wp-content\/uploads\/2021\/08\/unnamed-file-1024x683.jpg\" alt=\"Game over. LeBron James and Danny Green High Five each other. They are wearing special Kobe jerseys in the 2020 NBA Bubble.\" class=\"wp-image-163\" width=\"746\" height=\"497\" srcset=\"https:\/\/archive.gagvani.com\/wp-content\/uploads\/2021\/08\/unnamed-file-1024x683.jpg 1024w, https:\/\/archive.gagvani.com\/wp-content\/uploads\/2021\/08\/unnamed-file-300x200.jpg 300w, https:\/\/archive.gagvani.com\/wp-content\/uploads\/2021\/08\/unnamed-file-768x512.jpg 768w, https:\/\/archive.gagvani.com\/wp-content\/uploads\/2021\/08\/unnamed-file-195x130.jpg 195w, https:\/\/archive.gagvani.com\/wp-content\/uploads\/2021\/08\/unnamed-file.jpg 1486w\" sizes=\"(max-width: 746px) 100vw, 746px\" \/><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">The main() function &#8211; It&#8217;s all about the data<\/h2>\n\n\n\n<p>Finally, we have <code>main()<\/code>, the function Python actually calls when this code is run. As the heading says, a large part of this function is collecting the player&#8217;s data. As stated previously, I created a Python script that used Selenium to automatically find player&#8217;s data from <a href=\"http:\/\/stats.nba.com\" target=\"_blank\" rel=\"noreferrer noopener\">stats.nba.com<\/a>. It produces a SQL database as well as a CSV data file. For simplicity, we will be working with the CSV. Here&#8217;s what it looks like (If it&#8217;s too small on your screen, view it using the download button):<\/p>\n\n\n\n<div class=\"wp-block-file\"><a href=\"https:\/\/archive.gagvani.com\/wp-content\/uploads\/2021\/08\/players-1.csv\">players-1<\/a><a href=\"https:\/\/archive.gagvani.com\/wp-content\/uploads\/2021\/08\/players-1.csv\" class=\"wp-block-file__button\" download>Download<\/a><\/div>\n\n\n\n<figure class=\"wp-block-image size-large\"><img decoding=\"async\" width=\"1024\" height=\"335\" src=\"https:\/\/archive.gagvani.com\/wp-content\/uploads\/2021\/08\/image-1024x335.png\" alt=\"\" class=\"wp-image-165\" srcset=\"https:\/\/archive.gagvani.com\/wp-content\/uploads\/2021\/08\/image-1024x335.png 1024w, https:\/\/archive.gagvani.com\/wp-content\/uploads\/2021\/08\/image-300x98.png 300w, https:\/\/archive.gagvani.com\/wp-content\/uploads\/2021\/08\/image-768x251.png 768w, https:\/\/archive.gagvani.com\/wp-content\/uploads\/2021\/08\/image-1536x503.png 1536w, https:\/\/archive.gagvani.com\/wp-content\/uploads\/2021\/08\/image-2048x670.png 2048w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<p>We can see that each column header is a certain type of value, such as age, Paint_FGA, or name. We want to get this data into a bunch of Player objects, so each row will correspond with a Player. Now that we have figured out what to do, let&#8217;s see how it&#8217;s done. <\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: python; first-line: 563; title: ; notranslate\" title=\"\">\ndef main():\n    \n    #Getting REAL data\n    players=&#x5B;]#list of the objects of every NBA player\n    playernames=&#x5B;] #list of all player names FOR GUI\n<\/pre><\/div>\n\n\n<p>First, we define the main function, and then define two lists: <code>players <\/code>and <code><kbd>playernames<\/kbd><\/code>. The former will be passed into a <code>Game<\/code> object later, and the <code>playernames<\/code> will be shown in the GUI, so the user can select players to join each team. <\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: python; first-line: 569; title: ; notranslate\" title=\"\">\n    with open(&quot;players.csv&quot;) as rawData:\n        csv_reader=csv.DictReader(rawData,delimiter = &quot;,&quot;)\n        line_count=0\n        for row in csv_reader:\n            #print(row&#x5B;&quot;Name&quot;])\n            if &quot;-&quot; in row.values():\n                continue\n            playernames.append(row&#x5B;&quot;Name&quot;].strip())\n<\/pre><\/div>\n\n\n<p>First, we open a file reader object, named as rawData. Then we create a csv DictReader object which will allow us to easily parse the file, without having to write a lot of complex parsing and logic code. Then, it&#8217;s as simple as iterating over every row(remember, that&#8217;s every player) in the CSV file. If there is a dash in the row, indicating a blank value, that player is skipped with the <code>continue <\/code>operator. Then, the player&#8217;s name is appended to the <code>playernames <\/code>list. <code>.strip() <\/code>is a function which removes any extra whitespace in the front and back of a string. <\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: python; first-line: 577; title: ; notranslate\" title=\"\">\n            p=Player(row&#x5B;&quot;Name&quot;],row&#x5B;&quot;RestrictArea_FGpercent&quot;],row&#x5B;&quot;Paint_FGpercent&quot;],row&#x5B;&quot;MidRange_FGpercent&quot;],\n                     row&#x5B;&quot;LeftCorner3_FGpercent&quot;],row&#x5B;&quot;RightCorner3_FGpercent&quot;],row&#x5B;&quot;AboveBreak3_FGpercent&quot;],\n                     row&#x5B;&quot;RestrictArea_FGA&quot;],row&#x5B;&quot;Paint_FGA&quot;],row&#x5B;&quot;MidRange_FGA&quot;],row&#x5B;&quot;LeftCorner3_FGA&quot;], \n                     row&#x5B;&quot;RightCorner3_FGA&quot;],row&#x5B;&quot;Totals_FGA&quot;],row&#x5B;&quot;AboveBreak3_FGA&quot;],row&#x5B;&quot;RestrictArea_FGM&quot;],\n                     row&#x5B;&quot;Paint_FGM&quot;],row&#x5B;&quot;MidRange_FGM&quot;],row&#x5B;&quot;LeftCorner3_FGM&quot;],row&#x5B;&quot;RightCorner3_FGM&quot;],\n                     row&#x5B;&quot;AboveBreak3_FGM&quot;],row&#x5B;&quot;Totals_FGM&quot;])\n            players.append(p)\n<\/pre><\/div>\n\n\n<p>Next, we create a <code>Player <\/code>object <code>p<\/code>, and appended it to the list <code>players<\/code>. Of course, all that parameters that a Player requires is passed into the constructor through the CSV file reader. <\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: python; first-line: 604; title: ; notranslate\" title=\"\">\n    team1name=&quot;The Easy Breezies&quot;\n    team1_2pct2PA=0.5\n    team1_2pct3PA=0.5\n\n    team2name=&quot;The Jolly Follies&quot;\n    team2_2pct2PA=0.5\n    team2_2pct3PA=0.5\n    \n    poss= 200\n<\/pre><\/div>\n\n\n<p>Finally, we write the parameters for the teams and game. Try editing the code so that you can change the team name in the GUI! <\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: python; first-line: 614; title: ; notranslate\" title=\"\">\n    team1=Team(team1name,team1_2pct2PA,team1_2pct3PA)\n    team2=Team(team2name,team2_2pct2PA,team2_2pct3PA) \n    \n    teams=&#x5B;team1,team2]\n\n    game=Game(teams,players,poss) #initialize a game\n\n    #GUI\n    gui=GUI(game)\n\n    gui.add_players(playernames)\n    gui.start_gui()# gui starts in end because it takes over \n    \n\n\n\nif __name__ == &quot;__main__&quot;:\n    main()\n\n<\/pre><\/div>\n\n\n<p>Finally, we initialize each team, the game, and the GUI. The <code>if __name__ == \"__main__\"<\/code> section at the end allows us to know if the file is being run. Otherwise, the <code>main()<\/code> function will not run, and this code would have to be imported as a library to be called in a separate program. <\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Wrapping it up<\/h2>\n\n\n\n<p>You may have noticed that I haven&#8217;t covered the GUI class yet. That&#8217;s because a GUI is quite subjective with how the creator wants it to look like. I have actually written this both in Java and Python, and in both, the GUIs are extremely different. <\/p>\n\n\n\n<figure class=\"wp-block-gallery alignwide columns-2 is-cropped wp-block-gallery-1 is-layout-flex wp-block-gallery-is-layout-flex\"><ul class=\"blocks-gallery-grid\"><li class=\"blocks-gallery-item\"><figure><img decoding=\"async\" width=\"626\" height=\"633\" src=\"https:\/\/archive.gagvani.com\/wp-content\/uploads\/2021\/08\/allstarsim.jpg\" alt=\"\" data-id=\"170\" data-full-url=\"https:\/\/archive.gagvani.com\/wp-content\/uploads\/2021\/08\/allstarsim.jpg\" data-link=\"https:\/\/archive.gagvani.com\/?attachment_id=170\" class=\"wp-image-170\" srcset=\"https:\/\/archive.gagvani.com\/wp-content\/uploads\/2021\/08\/allstarsim.jpg 626w, https:\/\/archive.gagvani.com\/wp-content\/uploads\/2021\/08\/allstarsim-297x300.jpg 297w\" sizes=\"(max-width: 626px) 100vw, 626px\" \/><\/figure><\/li><li class=\"blocks-gallery-item\"><figure><img decoding=\"async\" width=\"707\" height=\"668\" src=\"https:\/\/archive.gagvani.com\/wp-content\/uploads\/2021\/08\/mba.jpg\" alt=\"\" data-id=\"171\" data-full-url=\"https:\/\/archive.gagvani.com\/wp-content\/uploads\/2021\/08\/mba.jpg\" data-link=\"https:\/\/archive.gagvani.com\/?attachment_id=171\" class=\"wp-image-171\" srcset=\"https:\/\/archive.gagvani.com\/wp-content\/uploads\/2021\/08\/mba.jpg 707w, https:\/\/archive.gagvani.com\/wp-content\/uploads\/2021\/08\/mba-300x283.jpg 300w\" sizes=\"(max-width: 707px) 100vw, 707px\" \/><\/figure><\/li><\/ul><figcaption class=\"blocks-gallery-caption\">On the left: The Java version. On the right: The Python version.<\/figcaption><\/figure>\n\n\n\n<p>As you can see, even though both of these GUIs accomplish the exact same functions, their visual layout is completely different. There are many design decisions that had to be made in order to deal with the constrains that both Java and Python GUIs allow. <\/p>\n\n\n\n<p>You can find the full code for the Python basketball simulator(Manav&#8217;s Basketball Association, or the MBA) on GitHub <a href=\"https:\/\/github.com\/mgagvani\/The-MBA\" target=\"_blank\" rel=\"noreferrer noopener\" title=\"https:\/\/github.com\/mgagvani\/The-MBA\">here<\/a>. This repo also contains the code for the Web detective which pulls the data. <\/p>\n\n\n\n<p>The <a href=\"https:\/\/github.com\/mgagvani\/allstarsimulator\" target=\"_blank\" rel=\"noreferrer noopener\" title=\"https:\/\/github.com\/mgagvani\/allstarsimulator\">Java version<\/a> contains some extra features such as a year selector. In this repo I&#8217;ve prepared data CSV files going from the 2011-12 season to the 2020-21 season. There are also executable JAR files under <a href=\"https:\/\/github.com\/mgagvani\/allstarsimulator\/releases\" target=\"_blank\" rel=\"noreferrer noopener\" title=\"https:\/\/github.com\/mgagvani\/allstarsimulator\/releases\">Releases<\/a> so you can try it out without compiling. Credit to <a href=\"https:\/\/github.com\/devkodre\" target=\"_blank\" rel=\"noreferrer noopener\" title=\"https:\/\/github.com\/devkodre\">Dev Kodre<\/a> for building the GUI for this project. <\/p>\n\n\n\n<p><strong>Thanks for reading! If you have any questions, don&#8217;t hesitate to put them in the comments!<\/strong><\/p>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"https:\/\/media1.giphy.com\/media\/VapWo1dvqJquP3frRb\/giphy.gif\" alt=\"Damian Lillard GIF\"\/><\/figure>\n","protected":false},"excerpt":{"rendered":"<p>Introduction Basketball is one of my primary interests. I&#8217;ve always been attracted to not only the highlight plays, but also the wealth of interesting statistical tidbits out there. Sites like FiveThirtyEight and others do a great job of digging up advanced stats to prove a point. After creating a program to automatically pull NBA stats [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":233,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[14,12,13],"tags":[15,16,17,18],"aioseo_notices":[],"_links":{"self":[{"href":"https:\/\/archive.gagvani.com\/index.php\/wp-json\/wp\/v2\/posts\/141"}],"collection":[{"href":"https:\/\/archive.gagvani.com\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/archive.gagvani.com\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/archive.gagvani.com\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/archive.gagvani.com\/index.php\/wp-json\/wp\/v2\/comments?post=141"}],"version-history":[{"count":30,"href":"https:\/\/archive.gagvani.com\/index.php\/wp-json\/wp\/v2\/posts\/141\/revisions"}],"predecessor-version":[{"id":197,"href":"https:\/\/archive.gagvani.com\/index.php\/wp-json\/wp\/v2\/posts\/141\/revisions\/197"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/archive.gagvani.com\/index.php\/wp-json\/wp\/v2\/media\/233"}],"wp:attachment":[{"href":"https:\/\/archive.gagvani.com\/index.php\/wp-json\/wp\/v2\/media?parent=141"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/archive.gagvani.com\/index.php\/wp-json\/wp\/v2\/categories?post=141"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/archive.gagvani.com\/index.php\/wp-json\/wp\/v2\/tags?post=141"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}