Some time at the tail end of my undergrad, I lit on an idea to calculate \(\pi\) in a novel way. Well, not too novel, I'm sure someone has thought of it before, but I wanted to try and do it myself. It was also a bit of neat practice for using the pygame
library, which is always nice.
The script basically works like this - we define a window, and more importantly a square in the dead center of the window. We also place a circle dead-center in that square, inscribed in it - basically just means we put a circle as large as possible that would fit inside the square. We draw these as long as the program runs.
Then, we enter a while True:
loop inside the program's main loop, where we keep selecting random points in the square, and keep track if they're inside the circle or not. Here's the thing - we know all of the random points we pick will be inside the square, we programmed them that way. We can also keep track of the proportion of "darts" we throw that end up inside the circle. This ratio will be huge for calculating \(pi\) this way.
Consider a square with side length \(2l\), and thus area \(4l^2\). If we inscribe a circle of maximum size inside this square, it will have a diameter \(2l\), or radius of length \(l\). We know the area of a circle is therefore \(\pi l^2\). So, in the long term, we can expect the ratio of darts inside the circle to darts we throw total to be:
\[\text{ratio}=\frac{\text{number of darts in the circle}}{\text{number of darts total}}=\frac{\pi l^2}{4l^2}=\frac{\pi}{4}\]By multiplying this ratio by 4, we can find a number which gets closer and closer to pi the more darts we throw!
# Dartboard Pi Calculation
import pygame, random, math
FPS = 60 # frames per second, the general speed of the program
WINDOWWIDTH = 640 # size of window's width in pixels
WINDOWHEIGHT = 480 # size of window's height in pixels
SQUAREWIDTH = 480
left = int((WINDOWWIDTH-SQUAREWIDTH)/2)
top = int((WINDOWHEIGHT-SQUAREWIDTH)/2)
half_width = int(WINDOWWIDTH/2)
half_height = int(WINDOWHEIGHT/2)
radius = int(SQUAREWIDTH/2)
# RGB values
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
RED = (255, 0, 0)
BGCOLOR = BLACK
CIRCLECOLOR = RED
OUTLINECOLOR = WHITE
BASICFONTSIZE = 15
def main():
global FPSCLOCK, DISPLAYSURF
pygame.init()
FPSCLOCK = pygame.time.Clock()
DISPLAYSURF = pygame.display.set_mode((WINDOWWIDTH, WINDOWHEIGHT))
pygame.display.set_caption('Calculating Pi with a Dartboard')
DISPLAYSURF.fill(BGCOLOR)
BASICFONT = pygame.font.Font('freesansbold.ttf', BASICFONTSIZE)
board = []
in_circle_count = 0
total = 0
while True:
x = random.randrange(left, left + SQUAREWIDTH)
y = random.randrange(WINDOWHEIGHT)
board.append([x,y])
if math.sqrt((x - (left + SQUAREWIDTH/2))**2 + (y - (top + SQUAREWIDTH/2))**2) < (SQUAREWIDTH/2):
in_circle_count += 1
total += 1
in_out_prop = in_circle_count/total
DISPLAYSURF.fill(BGCOLOR)
drawBoard(board, round(in_out_prop, 5), total, BASICFONT)
# Redraw the screen and wait for a clock tick
pygame.display.update()
FPSCLOCK.tick(FPS)
def drawBoard(board,in_prop,total,font):
textSurfs = []
pygame.draw.rect(DISPLAYSURF, OUTLINECOLOR, (left, top, SQUAREWIDTH, SQUAREWIDTH), 2)
pygame.draw.circle(DISPLAYSURF, OUTLINECOLOR, (half_width,half_height), radius, 2)
for coord in board:
pygame.draw.circle(DISPLAYSURF, CIRCLECOLOR, (coord[0], coord[1]), 2)
coord_last = board[-1]
textSurfs.append(font.render('x: ' + str(coord_last[0]), False, OUTLINECOLOR))
textSurfs.append(font.render('y: ' + str(coord_last[1]), False, OUTLINECOLOR))
textSurfs.append(font.render('p: ' + str(in_prop), False, OUTLINECOLOR))
textSurfs.append(font.render('pi: ' + str(4*in_prop), False, OUTLINECOLOR))
textSurfs.append(font.render('n: ' + str(total), False, OUTLINECOLOR))
n = 0
for surf in textSurfs:
textRect = surf.get_rect()
textRect.center = 40, 20 + n*20
DISPLAYSURF.blit(surf, textRect)
n += 1
if __name__ == '__main__':
main()