# Monte Carlo method of computing pi # with graphics showing the points on the unit circle # and printing he results in the graphics window # Toss darts at a dart board (2x2, centered at (0,0) # The ratio of the darts in the unit circle to all darts is pi/4 # # Matt Bishop, MHI 289I, Fall 2023 # import random import math import turtle # Generate a landing position for a dart tossed at # a 2x2 square with center at (0,0) # returns: (x, y) co-ordinates of toss def gentoss(): x = 2*random.random() - 1 y = 2*random.random() - 1 return x, y # for point x, y, if x^2 + y^2 <= 1, they are in the unit circle # parameters: x, y: co-ordinates of point # returns True if so, False if not def inunitcircle(x, y): return x ** 2 + y ** 2 <= 1 # function to read and vet user selection # returns: n, the number of tosses # NOTE: n must be positive; return -1 to quit def getinput(): # loop until we get good input while True: try: # get the input and check the type here n = int(input("number of tosses (EOF to quit): ")) except EOFError: # user wants to quit, so help n = -1 break except: # user didn't enter a number print("You have to enter a positive n or EOF") continue # got an integer # now check the value we read/were given if n > 0: break # this is bad, so say so print("You have to enter a positive n or EOF") # it's positive to continue, -1 to quit return n ##### GRAHICS ROUTINES # # # first, we need to scale everything to make it visible # set this by trial and error # scale = 100 # # make a window # parameters: col is background color of window # name is title to put on window # returns: handle for window # side effects: none # def makewindow(col, name): win = turtle.Screen() # draw the window for the picture win.bgcolor(col) # make the background color col win.title(name) # put up a title return win # # make a tutle # parameters: col is color of turtle (pen for drawing) # img is shape of turtle # returns: handle for turtle # side effects: animation is turned off and turtle is hidden # def maketurtle(col, img): tur = turtle.Turtle() # create a turtle tur.shape(img) # give it the identified shape tur.color(col) # be sure this color contrasts with the background tur.speed(0) # no animation -- turtle (drawing) just jumps tur.hideturtle() # don't show turtle return tur # # move turtle to a given point # parameters: tur is handle of turtle to use # x, y is new location of turtle # returns: nothing # side effects: none # def plotgoto(tur, x, y): tur.goto(x * scale, y * scale) # # draw a line from first point to second point # parameters: tur is handle of turtle to use # x1, y1 is beginning point of line # x2, y2 is ending point of line # returns: nothing # side effects: pen is raised to move turtle to line # leaves pen in original state at end # def plotline(tur, x1, y1, x2, y2): # get current pen state of turtle penstatedown = tur.isdown() # move the turtle to the starting point without drawing a line tur.penup() plotgoto(tur, x1, y1) # now draw the line tur.pendown() plotgoto(tur, x2, y2) # now restore original pen state if (not penstatedown): tur.penup() # # draw an x-axis and y-axis # parameters: tur is handle of turtle to use # xpos, xneg are the positive, negative end points of the x-axis # ypos, yneg are the positive, negative end points of the y-axis # returns: nothing # side effects: none # def plotaxes(tur, xpos, ypos, xneg, yneg): # draw the x axis plotline(tur, xneg, 0, xpos, 0) # draw the y axis plotline(tur, 0, yneg, 0, ypos) # # draw a dot at a point # parameters: tur is handle of turtle to use # x, y are co-ordinates of the point # returns: nothing # side effects: turtle raises pen throughout this # dot is 1 point in size # def plotpoint(tur, x, y): # get current pen state of turtle penstatedown = tur.isdown() # move the turtle to the point without drawing a line tur.penup() plotgoto(tur, x, y) # draw the dot tur.dot(1) # now restore original pen state if (penstatedown): tur.pendown() # # draw a unit circle, then surround it by a unit square # parameters: tur is handle of turtle to use # x, y are co-ordinates of the point # returns: nothing # side effects: turtle raises pen throughout this # dot is 1 point in size # def drawunit(tur, circlecolor, squarecolor): # get current color state of turtle curcolor = tur.pencolor() # first, draw the x, y axes (each 4 units, from -2 to 2) plotaxes(tur, 1, 1, -1, -1) # now draw a unit circle centered at (0, 0) in green tur.goto(0, -scale) tur.color(circlecolor) tur.circle(1 * scale) # now draw a square around the unit circle in red tur.color(squarecolor) plotline(tur, -1, -1, -1, 1) plotline(tur, -1, 1, 1, 1) plotline(tur, 1, 1, 1, -1) plotline(tur, 1, -1, -1, -1) # restore original color state tur.color(curcolor) ##### END OF GRAPHICS ROUTINES # main routine: pull it all together def main(): # get number of tosses n = getinput() # valid number; be sure it's positive, or user is stopping if n > 0: # initialize random number generator random.seed() # set up the window and the turtle wn = makewindow("white", "Plot of the Monte Carlo calculation of pi") yertle = maketurtle("black", "classic") # draw the figure drawunit(yertle, "green", "red") # number of darts in unit circle # nothing thrown yet h = 0 # now start throwing for i in range(n): # toss x, y = gentoss() # plot it plotpoint(yertle, x, y) # is it in the circle? if inunitcircle(x, y): h += 1 # done! see how well you did ... yertle.penup() yertle.goto(0, -1.25 * scale) yertle.write(" number of throws: %6d\t\tapproximation to pi: %9.7f" % (n, 4.0 * h / n), False, "center", ("Monaco", 10, "normal")) yertle.goto(0, -1.40 * scale) yertle.write(" number of hits: %6d\t\t actual value of pi: %9.7f" % (h, math.pi), False, "center", ("Monaco", 10, "normal")) yertle.goto(0, -1.55 * scale) yertle.write(" ratio: %6.4f\t\t error: %9.7f" % (h/n, abs(4.0 * h / n - math.pi)), False, "center", ("Monaco", 10, "normal")) # end it all yertle.hideturtle() wn.mainloop() # # they're off! # main()