Difference between revisions of "CSC111 Homework 6 Solution Programs 2011"

From dftwiki3
Jump to: navigation, search
(Program 3)
 
(2 intermediate revisions by the same user not shown)
Line 489: Line 489:
  
 
<br />
 
<br />
 +
==Program 3==
 +
* This version uses a list for the boxes as well as for the circles
 +
<br />
 +
<br />
 +
<source lang="python">
 +
# CSC 111 Thiebaut
 +
# hw6a.py
 +
# 111a-be
 +
# Naomi Long
 +
# Edited by D. Thiebaut
 +
#
 +
# This program shows two balls moving in the graphics window with three randomly
 +
# placed black rectangles that they can get trapped inside. One white rectangle
 +
# is generated that the balls reflect off of. Balls reverse direction when
 +
# colliding with the window. Both balls are given random colors and
 +
# random sizes.
 +
#
 +
# The initial location and speed of the balls are random, but such
 +
# that the balls appear in the window and are visible. It is possible
 +
# for a ball to have a Y speed of 0, so it only moves horizontally.
 +
#
 +
# If the balls end up inside of the black boxes or the range function runs for
 +
# 1000 cycles, the program ends.
 +
#
 +
# If the balls collide, they reverse motion in the x and y direction. If the
 +
# balls don't collide head-on, the collision looks fake. The balls can also
 +
# get stuck in collision loops where they travel the same path for the rest
 +
# of the program between two collisions, though this is unlikely because of the
 +
# black boxes and the white box.
 +
#
 +
# If a ball starts with 0 speed in the Y axis, when collided with it will
 +
# still keep 0 speed in that axis.
 +
#
 +
# If ball generates inside a box, it will be hidden for the rest of the program.
 +
# A ball can collide with another ball even if  theother ball is trapped in a
 +
# box.
 +
 +
from graphics import *
 +
import random
 +
from math import *
 +
 +
def distance( P1, P2 ):
 +
    """ computes distance for ball-on-ball collisions """
 +
    x = sqrt(pow(P1.getX() - P2.getX(), 2) + pow(P1.getY() - P2.getY(), 2))
 +
    return x
 +
 +
def isInside(c, r, offset ):
 +
    """ calculates whether a ball is inside a black box """
 +
    xc = c.getCenter().getX()
 +
    yc = c.getCenter().getY()
 +
    x1 = r.getP1().getX()
 +
    y1 = r.getP1().getY()
 +
    x2 = r.getP2().getX()
 +
    y2 = r.getP2().getY()
 +
    x1, x2 = min( x1, x2), max( x1, x2 )
 +
    y1, y2 = min( y1, y2), max( y1, y2 )
 +
    if ( ( x1 + offset <= xc <= x2 - offset )
 +
          and ( y1 + offset <= yc <= y2 - offset ) ):
 +
          return 1
 +
    return 0
 +
 +
def getNewBall( i, W, H ):
 +
    # defines colors balls can be
 +
    colors = ["red", "blue", "green", "yellow", "black", "white"]
 +
 +
    # draw 1st ball at random place with random velocity and random color
 +
    c1r = random.randrange(10, 20)
 +
    c1 = Circle(Point(random.randrange( c1r+1, W-c1r-1 ),  # random X
 +
                        random.randrange( c1r+1, H-c1r-1 ) ),  # random Y
 +
                        c1r)                                # random radius
 +
    c1color = random.choice( colors )
 +
    c1.setFill(c1color)
 +
    c1.setOutline(c1color)
 +
    dirX1 = random.randrange( 1, 5 )                        # random X speed
 +
    if ( random.randrange( 0, 100 ) ) < 50:
 +
        dirX1 = -dirX1
 +
    dirY1 = random.randrange( 1, 5 )                        # random Y speed
 +
    if ( random.randrange( 0, 100 ) ) < 50:
 +
        dirY1 = -dirY1
 +
    return c1, dirX1, dirY1
 +
       
 +
         
 +
def main():
 +
    """ opens a graphics window """
 +
    W = 600
 +
    H = 400
 +
    win = GraphWin("111a-be's billiard program", W, H)
 +
    win.setBackground(color_rgb(25, 25, 112))
 +
    noBoxes = 3
 +
    noCircles = 5
 +
   
 +
    # define random coordinates for black boxes
 +
    boxes = []
 +
    for i in range( noBoxes ):
 +
        x1 = 10 + i * W // noBoxes
 +
        x2 = x1 + W // (noBoxes * 2 )
 +
        y1 = 10 + i * H // noBoxes
 +
        y2 = x1 + H // (noBoxes * 2 )
 +
        boxes.append( Rectangle( Point( x1, y1), Point( x2, y2 ) ) )                     
 +
        boxes[i].setFill("black")
 +
        boxes[i].draw(win)
 +
 +
 +
    # define size of white box
 +
    ws1 = 50
 +
 +
    # define random coordinates for white box
 +
    wp1a = random.randrange(10, W//3 - (ws1 + 20))
 +
    wp1b = random.randrange(H//2 + 40, H - 40)
 +
    WP1 = Point(wp1a, wp1b)
 +
    WP2 = Point(wp1a + ws1, wp1b + ws1)
 +
 +
    # draw white box
 +
    wr = Rectangle( Point( 10, H-10 ), Point( 10+W/(noBoxes*2), H-10-H//(noBoxes*2) ) )
 +
    wr.setFill("white")
 +
    wr.setWidth(3)
 +
    wr.draw(win)
 +
 +
    # create circles
 +
    circles = []
 +
    for i in range( noCircles ):
 +
        c1, dirX1, dirY1 = getNewBall( i, W, H )
 +
        c1.draw( win )
 +
        circles.append( [c1, dirX1, dirY1 ] )
 +
 +
    # moving balls and collisions
 +
    for i in range(1000):
 +
 
 +
        # moves balls at velocities given
 +
        for i in range( noCircles ):
 +
            c1, dirX1, dirY1 = circles[ i ]
 +
            c1.move( dirX1, dirY1 )
 +
 +
        # make c1 bounce off of walls
 +
        for i in range( len( circles ) ):
 +
            c1, dirX1, dirY1 = circles[ i ]
 +
            if c1.getCenter().getX() < c1.getRadius() + 4 or c1.getCenter().getX() > W - c1.getRadius():
 +
                  dirX1 = -dirX1
 +
                  #print( "1" )
 +
            if c1.getCenter().getY() < c1.getRadius() + 4 or c1.getCenter().getY() > H - c1.getRadius():
 +
                  dirY1 = -dirY1
 +
                  #print( "2" )
 +
            circles[ i ] = [ c1, dirX1, dirY1 ]
 +
           
 +
 +
        # make balls reverse direction when they collide
 +
        for i in range( len( circles ) ):
 +
            for j in range( i+1, len( circles ) ):
 +
                c1, dirX1, dirY1 = circles[ i ]
 +
                c2, dirX2, dirY2 = circles[ j ]
 +
                dist = distance( c1.getCenter(), c2.getCenter())
 +
                if dist <= (c1.getRadius() + c2.getRadius() ):
 +
                      dirX1 = -dirX1
 +
                      dirY1 = -dirY1
 +
                      dirX2 = -dirX2
 +
                      dirY2 = -dirY2
 +
                      circles[ i ] = [ c1, dirX1, dirY1 ]
 +
                      circles[ j ] = [ c2, dirX2, dirY2 ]
 +
                      #print( "3", dist )
 +
 +
        # make ball 1 bounce off of white box
 +
        for i in range( len( circles ) ):
 +
            c1, dirX1, dirY1 = circles[ i ]
 +
            if isInside(c1, wr, -c1.getRadius() ) == 1:
 +
                  dirX1 = -dirX1
 +
                  dirY1 = -dirY1
 +
                  circles[ i ] = [ c1, dirX1, dirY1 ]
 +
                  #print( "4" )
 +
 +
 +
        # make ball 1 catch in black boxes
 +
        for i in range( len( circles ) ):
 +
            c1, dirX1, dirY1 = circles[ i ]
 +
            for r in boxes:
 +
                if isInside(c1, r, c1.getRadius() ) == 1:
 +
                        dirX1 = 0
 +
                        dirY1 = 0
 +
                        circles[ i ] = [ c1, dirX1, dirY1 ]
 +
                        #print( "5" )
 +
                        break
 +
 +
        # end program if both balls are stuck in black boxes
 +
        count = 0
 +
        for c1, dirX1, dirY1 in circles:
 +
            if (dirX1 == 0 and dirY1 == 0): 
 +
                  count = count + 1
 +
 +
        # wait for one more click and close up window
 +
        if count == len( circles ):
 +
            t1 = Text(Point(W//2, H//2), "Click me to quit")
 +
            t1.setFill("white")
 +
            t1.draw(win)
 +
            win.getMouse()
 +
            win.close()
 +
            return
 +
 +
main()
 +
 +
</source>
 +
<br />
 +
<br />
 +
 
=Statitics=
 
=Statitics=
  

Latest revision as of 20:26, 7 November 2011

--D. Thiebaut 15:33, 7 November 2011 (EST)


Moving Balls

Program 1



# CSC 111 Thiebaut
# hw6a.py
# 111a-be
# Naomi Long
#
# This program shows two balls moving in the graphics window with three randomly
# placed black rectangles that they can get trapped inside. One white rectangle
# is generated that the balls reflect off of. Balls reverse direction when
# colliding with the window. Both balls are given random colors and
# random sizes.
#
# The initial location and speed of the balls are random, but such
# that the balls appear in the window and are visible. It is possible
# for a ball to have a Y speed of 0, so it only moves horizontally.
#
# If the balls end up inside of the black boxes or the range function runs for
# 1000 cycles, the program ends.
#
# If the balls collide, they reverse motion in the x and y direction. If the
# balls don't collide head-on, the collision looks fake. The balls can also
# get stuck in collision loops where they travel the same path for the rest
# of the program between two collisions, though this is unlikely because of the
# black boxes and the white box.
#
# If a ball starts with 0 speed in the Y axis, when collided with it will
# still keep 0 speed in that axis.
#
# If ball generates inside a box, it will be hidden for the rest of the program.
# A ball can collide with another ball even if  theother ball is trapped in a
# box.

from graphics import *
import random
from math import *

def distance( P1, P2 ):
     # computes distance for ball-on-ball collisions
     x = sqrt(pow(P1.getX() - P2.getX(), 2) + pow(P1.getY() - P2.getY(), 2))
     return x

def isInside(c, r, P1, P2, cr):
     # calculates whether a ball is inside a black box
     xc = c.getCenter().getX()
     yc = c.getCenter().getY()
     x1 = P1.getX()
     y1 = P1.getY()
     x2 = P2.getX()
     y2 = P2.getY()
     if (x1 + cr <= xc <= x2 - cr) and (y1 + cr <= yc <= y2 - cr):
          return 0
     
def bounce(c, r, P1, P2, cr):
     # calculates if outside edges of ball are touching white box
     xc = c.getCenter().getX()
     yc = c.getCenter().getY()
     x1 = P1.getX()
     y1 = P1.getY()
     x2 = P2.getX()
     y2 = P2.getY()
     if (x1 - cr <= xc <= x2 + cr) and (y1 - cr <= yc <= y2 + cr):
          return 0
 
def main():
    # opens a graphics window
    W = 600
    H = 400
    win = GraphWin("111a-be's billiard program", W, H)
    win.setBackground(color_rgb(25, 25, 112))

    # defines colors balls can be
    colors = ["red", "blue", "green", "yellow", "black", "white"]
 
    # draw 1st ball at random place with random velocity and random color
    c1r = random.randrange(10, 20)
    c1 = Circle(Point(random.randrange(50, W//2 - 50),      # random X
                        random.randrange(50, H//2 - 50)),   # random Y
                        c1r)                                # random radius
    c1color = colors[random.randrange(5)]                   # random color
    c1.setFill(c1color)
    c1.setOutline(c1color)
    dirX1 = random.randrange(-5, 5)                         # random X speed
    if dirX1 == 0:                                          # X can't be 0
         dirX1 = dirX1 + 1
    dirY1 = random.randrange(-5, 5)                         # random Y speed
    c1.draw(win)
 
    # draw 2nd ball at random place with random velocity and random color
    c2r = random.randrange(10, 20)
    c2 = Circle(Point(random.randrange(W//2, W-50),         # random X
                        random.randrange(H//2, H-50)),      # random Y
                       c2r)                                 # random radius
    c2color = colors[random.randrange(5)]                   # random color
    c2.setFill(c2color)
    c2.setOutline(c2color)
    dirX2 = random.randrange(-5, 5)                         # random X speed
    if dirX2 == 0:                                          # X can't be 0
         dirX2 = dirX2 + 1
    dirY2 = random.randrange(-5, 5)                         # random Y speed
    c2.draw(win)

    # define sizes for boxes
    s1 = 70
    s2 = 80
    s3 = 60

    # define random coordinates for black boxes
    p1a = random.randrange(10, W//3 - (s1 + 20))
    p1b = random.randrange(10, H//2 - (s1 + 20))
    P1 = Point(p1a, p1b)
    P2 = Point(p1a + s1, p1b + s1)
    p3a = random.randrange(W//3 + 10, (2 * W // 3) - (s2 + 20))
    p3b = random.randrange(H//3 + 10, (2 * H // 3) - (s2 + 20))
    P3 = Point(p3a, p3b)
    P4 = Point(p3a + s2, p3b + s2)
    p5a = random.randrange((2 * W // 3) + 20, W - (s3 + 20))
    p5b = random.randrange((2 * H // 3) + 20, H - (s3 + 20))
    P5 = Point(p5a, p5b)
    P6 = Point(p5a + s3, p5b + s3)

    # arranges points in rectangles
    boxes = []
    r = Rectangle( P1, P2 )
    boxes.append(r)
    r = Rectangle( P3, P4 )
    boxes.append(r)
    r = Rectangle( P5, P6 )
    boxes.append(r)

    # draws black boxes
    for i in range(len(boxes)):
         boxes[i].setFill("black")
         boxes[i].draw(win)

    # define size of white box
    ws1 = 50

    # define random coordinates for white box
    wp1a = random.randrange(10, W//3 - (ws1 + 20))
    wp1b = random.randrange(H//2 + 40, H - 40)
    WP1 = Point(wp1a, wp1b)
    WP2 = Point(wp1a + ws1, wp1b + ws1)

    # draw white box
    wr = Rectangle(WP1, WP2)
    wr.setFill("white")
    wr.setWidth(3)
    wr.draw(win)

    # moving balls and collisions
    for i in range(1000):
         
         stuck = 0
         
         # moves balls at velocities given
         c1.move(dirX1, dirY1)
         c2.move(dirX2, dirY2)

         # make c1 bounce off of walls
         if c1.getCenter().getX() < c1r + 4 or c1.getCenter().getX() > W - c1r:
              dirX1 = -dirX1
         if c1.getCenter().getY() < c1r + 4 or c1.getCenter().getY() > H - c1r:
              dirY1 = -dirY1

         # make c2 bounce off of walls
         if c2.getCenter().getX() < c2r + 4 or c2.getCenter().getX() > W - c2r:
              dirX2 = -dirX2
         if c2.getCenter().getY() < c2r + 4 or c2.getCenter().getY() > H - c2r:
              dirY2 = -dirY2

         # make balls reverse direction when they collide
         if distance(c1.getCenter(), c2.getCenter()) <= (c1r + c2r):
              dirX1 = -dirX1
              dirY1 = -dirY1
              dirX2 = -dirX2
              dirY2 = -dirY2
              
         # make ball 1 bounce off of white box
         if bounce(c1, wr, WP1, WP2, c1r) == 0:
              dirX1 = -dirX1
              dirY1 = -dirY1
              
         # make ball 2 bounce off of white box
         if bounce(c2, wr, WP1, WP2, c2r) == 0:
              dirX2 = -dirX2
              dirY2 = -dirY2

         # make ball 1 catch in black boxes
         if isInside(c1, r, P1, P2, c1r) == 0:
              dirX1 = 0
              dirY1 = 0
         if isInside(c1, r, P3, P4, c1r) == 0:
              dirX1 = 0
              dirY1 = 0
         if isInside(c1, r, P5, P6, c1r) == 0:
              dirX1 = 0
              dirY1 = 0

         # make ball 2 catch in black boxes
         if isInside(c2, r, P1, P2, c2r) == 0:
              dirX2 = 0
              dirY2 = 0
         if isInside(c2, r, P3, P4, c2r) == 0:
              dirX2 = 0
              dirY2 = 0
         if isInside(c2, r, P5, P6, c2r) == 0:
              dirX2 = 0
              dirY2 = 0

         # end program if both balls are stuck in black boxes
         if (dirX1 == 0 and dirY1 == 0) and (dirX2 == 0 and dirY2 == 0):
              t = Text(Point(W//2, H//2), "Click me to quit")
              t.setFill("white")
              t.draw(win)
              win.getMouse()
              win.close()
              stuck = 1
              break
     
    # wait for one more click and close up window
    if stuck != 1:
         t1 = Text(Point(W//2, H//2), "Click me to quit")
         t1.setFill("white")
         t1.draw(win)
         win.getMouse()
         win.close()
 
main()




Program 2

  • This version implements fully elastic collision and respects the laws of physics, even giving the two balls a mass proportional to their radii.



#hw6a.py
#111a-ar
#Gavi Levy Haskell
#
#This program is an adaptation of the hw6a Demo Program.
#Two balls, one turquoise and one blue, bounce off the
#walls, each other, and a white box, and stop if they
#completely enter one of three black boxes, or if the
#program goes through 1000 steps.
#
#
#NOTE: The circles bounce "realistically" assuming that:
#
#The speeds never exceed those which Newton's equations
#work for
#
#All collisions occur along the x-y axis (not true for
#virtually any collision, but a fair approximation for
#most blows, less good for glancing blows)
#
#The masses of the circles are relative to each other,
#imagining them to be 2D representations of 3D figures
#(spheres, to be precise)
#
#The world has no friction, either between objects or
#with the ground
#
#The collisions are perfectly elastic


from graphics import *
import random

def distance(P1, P2):
    """distance formula
    """
    return (( (P1.getX() - P2.getX())**2 +
             (P1.getY() - P2.getY())**2)**.5)

def velocity():
    """picks a random velocity
       between -5 and 5, not 0
    """
    v = 0
    while True:
        v = random.randrange(-5, 5)
        if v!=0:
            return v

def main():
    #sets up graphics window
    w   = 600
    h   = 300
    win = GraphWin("111a-ar: Ten to the Fifteenth", w, h)
    win.setBackground("black")

    #picks random radii
    r1  = random.randrange(10, 20) #radius
    r2  = random.randrange(10, 20) #radius

    #calls velocity for all four velocities of the
    #circle
    vx1 = velocity()
    vy1 = velocity()
    vx2 = velocity()
    vy2 = velocity()


    #draws circles at random locations
    c1  = Circle(Point(random.randrange(r1, w//2),
                       random.randrange(r1, h//2 - r1)),
                 r1)
    c1.setWidth(0)
    c1.setFill("turquoise")
    c1.draw(win)

    c2  = Circle(Point(random.randrange(r2, w//2),
                       random.randrange(h//2, h - r2)),
                 r2)
    c2.setWidth(0)
    c2.setFill("blue")
    c2.draw(win)

    #draws white box
    rw = Rectangle(Point(400, 200),
                   Point(500, 250))
    rw.setWidth(0)
    rw.setFill("white")
    rw.draw(win)

    #draws three black boxes
    boxes = [[10, 240, 80, 290],
             [250, 80, 350, 130],
             [520, 40, 570, 120]]
    
    for i in range(3):
        a, b, c, d = boxes[i]
        rb = Rectangle(Point(a,b), Point(c,d))
        rb.setOutline("white")
        rb.setWidth(2)
        rb.draw(win)


    #loop to move circles
    for i in range(1000):
        #assigns locations of circles to variables
        x1 = c1.getCenter().getX()
        y1 = c1.getCenter().getY()
        x2 = c2.getCenter().getX()
        y2 = c2.getCenter().getY()

        #variable to allow for turning off inter-
        #action between circles when in black box
        n = 0


        #traps circles in black boxes
        for j in range(3):
            a,b,c,d = boxes[j]
            
            #margin for circle 1
            xl1 = a + r1
            yu1 = b + r1
            xr1 = c - r1
            yb1 = d - r1
            #if circle is inside box
            if xl1 < x1 < xr1 and yu1 < y1 < yb1:
                vx1 = 0   #set velocities
                vy1 = 0   #to zero
                n   = 1   #indicates circle is in box
                
            #margin for circle 2
            xl2 = a + r2
            yu2 = b + r2
            xr2 = c - r2
            yb2 = d - r2
            #if circle is inside box
            if xl2 < x2 < xr2 and yu2 < y2 < yb2:
                vx2 = 0   #set velocities
                vy2 = 0   #to zero
                n   = 1   #indicates circle is in box


        #breaks if circles have been trapped
        if vx1 == 0 and vy1 == 0 and vx2 == 0 and vy2 == 0:
            break


        #lets circles bounce off white box
        #FIRST CIRCLE
        lb1 = 400 - r1 #left bound for first circle
        rb1 = 500 + r1 #right bound
        ub1 = 200 - r1 #upper bound
        bb1 = 250 + r1 #lower bound
        if lb1 < x1 < rb1 and ub1 < y1 < bb1:
            if 400 > x1:        #on left side
                vx1 = -abs(vx1) #bounce left
            if 500 < x1:        #on right side
                vx1 = abs(vx1)  #bounce right
            if 200 > y1:        #above
                vy1 = -abs(vy1) #bounce up
            if 250 < y1:        #below
                vy1 = abs(vy1)  #bounce down
                
        #SECOND CIRCLE
        lb2 = 400 - r2 #left bound for second circle
        rb2 = 500 + r2 #right bound
        ub2 = 200 - r2 #upper bound
        bb2 = 250 + r2 #lower bound
        if lb2 < x2 < rb2 and ub2 < y2 < bb2:
            if 400 > x2:        #on left side
                vx2 = -abs(vx2) #bounce left
            if 500 < x2:        #on right side
                vx2 = abs(vx2)  #bounce right
            if 200 > y2:        #above
                vy2 = -abs(vy2) #bounce up
            if 250 < y2:        #below
                vy2 = abs(vy2)  #bounce down
                

        #lets circles bounce off walls (w/o sticking)
        #circle 1
        if x1 < r1 + 5: #left side
            vx1 = abs(vx1)
        if x1 > w - r1: #right side
            vx1 = -abs(vx1)
        if y1 < r1 + 5: #top
            vy1 = abs(vy1)
        if y1 > h - r1: #bottom
            vy1 = -abs(vy1)
        #circle 2
        if x2 < r2 + 5: #left side
            vx2 = abs(vx2)
        if x2 > w - r2: #right side
            vx2 = -abs(vx2)
        if y2 < r2 + 5: #top
            vy2 = abs(vy2)
        if y2 > h - r2: #bottom
            vy2 = -abs(vy2)

        #lets circles bounce off each other
        #distance between circles
        dc = distance(c1.getCenter(), c2.getCenter())
        
        if dc <= (r1 + r2) and n == 0:
            #"masses" of circles (relative, 3D)
            m1  = r1*r1*r1
            m2  = r2*r2*r2
            
            #elastic collisions
            a1 = (vx1*(m1 - m2) + 2*m2*vx2)/(m1 + m2)
            a2 = (vx2*(m2 - m1) + 2*m1*vx1)/(m1 + m2)
            b1 = (vy1*(m1 - m2) + 2*m2*vy2)/(m1 + m2)
            b2 = (vy2*(m2 - m1) + 2*m1*vy1)/(m1 + m2)

            vx1 = a1
            vx2 = a2
            vy1 = b1
            vy2 = b2

        #moves circles
        c1.move(vx1, vy1)
        c2.move(vx2, vy2)

        

    text = Text(Point(w//2, h//2), "Click to quit")
    text.setFill("white")
    text.draw(win)
    win.getMouse()
    win.close()

main()




Program 3

  • This version uses a list for the boxes as well as for the circles



# CSC 111 Thiebaut
# hw6a.py
# 111a-be
# Naomi Long
# Edited by D. Thiebaut
#
# This program shows two balls moving in the graphics window with three randomly
# placed black rectangles that they can get trapped inside. One white rectangle
# is generated that the balls reflect off of. Balls reverse direction when
# colliding with the window. Both balls are given random colors and
# random sizes.
#
# The initial location and speed of the balls are random, but such
# that the balls appear in the window and are visible. It is possible
# for a ball to have a Y speed of 0, so it only moves horizontally.
#
# If the balls end up inside of the black boxes or the range function runs for
# 1000 cycles, the program ends.
#
# If the balls collide, they reverse motion in the x and y direction. If the
# balls don't collide head-on, the collision looks fake. The balls can also
# get stuck in collision loops where they travel the same path for the rest
# of the program between two collisions, though this is unlikely because of the
# black boxes and the white box.
#
# If a ball starts with 0 speed in the Y axis, when collided with it will
# still keep 0 speed in that axis.
#
# If ball generates inside a box, it will be hidden for the rest of the program.
# A ball can collide with another ball even if  theother ball is trapped in a
# box.
 
from graphics import *
import random
from math import *
 
def distance( P1, P2 ):
     """ computes distance for ball-on-ball collisions """
     x = sqrt(pow(P1.getX() - P2.getX(), 2) + pow(P1.getY() - P2.getY(), 2))
     return x
 
def isInside(c, r, offset ):
     """ calculates whether a ball is inside a black box """
     xc = c.getCenter().getX()
     yc = c.getCenter().getY()
     x1 = r.getP1().getX()
     y1 = r.getP1().getY()
     x2 = r.getP2().getX()
     y2 = r.getP2().getY()
     x1, x2 = min( x1, x2), max( x1, x2 )
     y1, y2 = min( y1, y2), max( y1, y2 )
     if ( ( x1 + offset <= xc <= x2 - offset ) 
          and ( y1 + offset <= yc <= y2 - offset ) ):
          return 1
     return 0

def getNewBall( i, W, H ):
    # defines colors balls can be
    colors = ["red", "blue", "green", "yellow", "black", "white"]
 
    # draw 1st ball at random place with random velocity and random color
    c1r = random.randrange(10, 20)
    c1 = Circle(Point(random.randrange( c1r+1, W-c1r-1 ),   # random X
                        random.randrange( c1r+1, H-c1r-1 ) ),   # random Y
                        c1r)                                # random radius
    c1color = random.choice( colors )
    c1.setFill(c1color)
    c1.setOutline(c1color)
    dirX1 = random.randrange( 1, 5 )                        # random X speed
    if ( random.randrange( 0, 100 ) ) < 50:
         dirX1 = -dirX1
    dirY1 = random.randrange( 1, 5 )                        # random Y speed
    if ( random.randrange( 0, 100 ) ) < 50:
         dirY1 = -dirY1
    return c1, dirX1, dirY1
         
          
def main():
    """ opens a graphics window """
    W = 600
    H = 400
    win = GraphWin("111a-be's billiard program", W, H)
    win.setBackground(color_rgb(25, 25, 112))
    noBoxes = 3
    noCircles = 5
    
    # define random coordinates for black boxes
    boxes = []
    for i in range( noBoxes ):
        x1 = 10 + i * W // noBoxes
        x2 = x1 + W // (noBoxes * 2 )
        y1 = 10 + i * H // noBoxes
        y2 = x1 + H // (noBoxes * 2 )
        boxes.append( Rectangle( Point( x1, y1), Point( x2, y2 ) ) )                      
        boxes[i].setFill("black")
        boxes[i].draw(win)
 
 
    # define size of white box
    ws1 = 50
 
    # define random coordinates for white box
    wp1a = random.randrange(10, W//3 - (ws1 + 20))
    wp1b = random.randrange(H//2 + 40, H - 40)
    WP1 = Point(wp1a, wp1b)
    WP2 = Point(wp1a + ws1, wp1b + ws1)
 
    # draw white box
    wr = Rectangle( Point( 10, H-10 ), Point( 10+W/(noBoxes*2), H-10-H//(noBoxes*2) ) )
    wr.setFill("white")
    wr.setWidth(3)
    wr.draw(win)

    # create circles
    circles = []
    for i in range( noCircles ):
        c1, dirX1, dirY1 = getNewBall( i, W, H )
        c1.draw( win )
        circles.append( [c1, dirX1, dirY1 ] )
 
    # moving balls and collisions
    for i in range(1000):
  
         # moves balls at velocities given
         for i in range( noCircles ):
             c1, dirX1, dirY1 = circles[ i ]
             c1.move( dirX1, dirY1 )
 
         # make c1 bounce off of walls
         for i in range( len( circles ) ):
             c1, dirX1, dirY1 = circles[ i ] 
             if c1.getCenter().getX() < c1.getRadius() + 4 or c1.getCenter().getX() > W - c1.getRadius():
                  dirX1 = -dirX1
                  #print( "1" )
             if c1.getCenter().getY() < c1.getRadius() + 4 or c1.getCenter().getY() > H - c1.getRadius():
                  dirY1 = -dirY1
                  #print( "2" )
             circles[ i ] = [ c1, dirX1, dirY1 ]
             
 
         # make balls reverse direction when they collide
         for i in range( len( circles ) ):
             for j in range( i+1, len( circles ) ):
                 c1, dirX1, dirY1 = circles[ i ]
                 c2, dirX2, dirY2 = circles[ j ]
                 dist = distance( c1.getCenter(), c2.getCenter()) 
                 if dist <= (c1.getRadius() + c2.getRadius() ):
                      dirX1 = -dirX1
                      dirY1 = -dirY1
                      dirX2 = -dirX2
                      dirY2 = -dirY2
                      circles[ i ] = [ c1, dirX1, dirY1 ]
                      circles[ j ] = [ c2, dirX2, dirY2 ]
                      #print( "3", dist )

         # make ball 1 bounce off of white box
         for i in range( len( circles ) ):
             c1, dirX1, dirY1 = circles[ i ] 
             if isInside(c1, wr, -c1.getRadius() ) == 1:
                  dirX1 = -dirX1
                  dirY1 = -dirY1
                  circles[ i ] = [ c1, dirX1, dirY1 ] 
                  #print( "4" )

 
         # make ball 1 catch in black boxes
         for i in range( len( circles ) ):
             c1, dirX1, dirY1 = circles[ i ]
             for r in boxes:
                 if isInside(c1, r, c1.getRadius() ) == 1:
                        dirX1 = 0
                        dirY1 = 0
                        circles[ i ] = [ c1, dirX1, dirY1 ]
                        #print( "5" )
                        break
 
         # end program if both balls are stuck in black boxes
         count = 0
         for c1, dirX1, dirY1 in circles:
             if (dirX1 == 0 and dirY1 == 0):  
                  count = count + 1
 
         # wait for one more click and close up window
         if count == len( circles ):
             t1 = Text(Point(W//2, H//2), "Click me to quit")
             t1.setFill("white")
             t1.draw(win)
             win.getMouse()
             win.close()
             return
 
main()



Statitics



#hw6b.py
#111a-ar
#Gavi Levy Haskell
#
#Prints formatted top ten lists by population, internet
#users, ratio of active users to population, and some
#global statistics based on a list of countries and
#their attributes.


#list of statistics about internet usage in countries
statistics = [['Afghanistan', 28710000, -1, -1, 1],
              ['Albania', 3580000, 12000, -1, 10],
              ['Algeria', 32810000, 180000, -1, 2],
              ['Andorra', 69150, 24500, -1, 1],
              ['Angola', 10760000, 60000, -1, 1],
              ['Anguilla', 12738, 919, -1, 16],
              ['Antigua_and_Barbuda', 67897, 5000, -1, 16],
              ['Argentina', 38740000, 4650000, -1, 33],
              ['Armenia', 3320000, 30000, -1, 9],
              ['Aruba', 70844, 24000, -1, -1],
              ['Australia', 19730000, 13010000, 9020000, 571],
              ['Austria', 8180000, 4650000, 1300000, 37],
              ['Azerbaijan', 7830000, 25000, -1, 2],
              ['Bahrain', 667238, 140200, -1, 1],
              ['Bangladesh', 138440000, 150000, -1, 10],
              ['Barbados', 277264, 6000, -1, 19],
              ['Belarus', 10330000, 422000, -1, 23],
              ['Belgium', 10280000, 4870000, 1600000, 61],
              ['Belize', 266440, 18000, -1, 2],
              ['Benin', 7040000, 25000, -1, 4],
              ['Bhutan', 2130000, 2500, -1, -1],
              ['Bolivia', 8580000, 78000, -1, 9],
              ['Bosnia_and_Herzegovian', 3980000, 45000, -1, 3],
              ['Botswana', 1570000, 33000, -1, 11],
              ['Brazil', 182030000, 22320000, 10860000, 50],
              ['Brunei', 358098, 35000, -1, 2],
              ['Bulgaria', 7530000, 1610000, -1, 200],
              ['Burkina_Faso', 13220000, 25000, -1, 1],
              ['Burma', 42510000, 10000, -1, 1],
              ['Burundi', 6090000, 6000, -1, 1],
              ['Cambodia', 13120000, 10000, -1, 2],
              ['Cameroon', 15740000, 45000, -1, 1],
              ['Canada', 32200000, 20450000, 8800000, 760],
              ['Cape_Verde', 412137, 12000, -1, 1],
              ['Cayman_Islands', 41934, -1, -1, 16],
              ['Central_African_Republic', 3680000, 2000, -1, 1],
              ['Chad', 9250000, 4000, -1, 1],
              ['Chile', 15660000, 5040000, -1, 7],
              ['China', 1280000000, 99800000, -1, 3],
              ['Colombia', 41660000, 1870000, -1, 18],
              ['Comoros', 632948, 2500, -1, 1],
              ['Republic_of_the_Congo_Democratic', 56620000, 6000, -1, 1],
              ['Congo_Republic', 2950000, 500, -1, 1],
              ['Cook_Islands', 21008, -1, -1, 3],
              ['Costa_Rica', 3890000, 384000, -1, 3],
              ['Cote_d_Ivoire', 16960000, 70000, -1, 5],
              ['Croatia', 4420000, 480000, -1, 9],
              ['Cuba', 11260000, 120000, -1, 5],
              ['Cyprus', 771657, 150000, -1, 6],
              ['Czech_Republic', 10240000, 3530000, -1, 300],
              ['Denmark', 5380000, 3720000, -1, 13],
              ['Djibouti', 457130, 3300, -1, 1],
              ['Dominica', 69655, 2000, -1, 16],
              ['Dominican_Republic', 8710000, 186000, -1, 24],
              ['East_Timor', 977853, -1, -1, -1],
              ['Ecuador', 13710000, 328000, -1, 31],
              ['Egypt', 74710000, 2420000, -1, 50],
              ['El_Salvador', 6470000, 40000, -1, 4],
              ['Equatorial_Guinea', 510473, 900, -1, 1],
              ['Eritrea', 4360000, 10000, -1, 5],
              ['Estonia', 1400000, 620000, -1, 38],
              ['Ethiopia', 66550000, 20000, -1, 1],
              ['Faroe_Islands', 46345, 3000, -1, 2],
              ['Fiji', 856300, 15000, -1, 2],
              ['Finland', 5190000, 3270000, -1, 3],
              ['France', 60180000, 25470000, 15530000, 62],
              ['French_Guiana', 182917, 2000, -1, 2],
              ['French_Polynesia', 262125, 16000, -1, 2],
              ['Gabon', 1320000, 18000, -1, 1],
              ['Gambia', 1500000, 5000, -1, 2],
              ['Georgia', 4930000, 25000, -1, 6],
              ['Germany', 82390000, 41880000, 29520000, 200],
              ['Ghana', 20460000, 200000, -1, 12],
              ['Gilbraltar', 27776, -1, -1, 2],
              ['Greece', 10660000, 2710000, -1, 27],
              ['Greenland', 56385, 20000, -1, 1],
              ['Grenada', 89258, 5200, -1, 14],
              ['Guadeloupe', 440189, 4000, -1, 3],
              ['Guam', 163941, 5000, -1, 20],
              ['Guatemala', 13900000, 200000, -1, 5],
              ['Guernsey', 64818, -1, -1, -1],
              ['Guinea', 9030000, 15000, -1, 4],
              ['Guinea-Bissau', 1360000, 4000, -1, 2],
              ['Guyana', 702100, 95000, -1, 3],
              ['Haiti', 7520000, 30000, -1, 3],
              ['Honduras', 6660000, 40000, -1, 8],
              ['Hong_Kong', 7390000, 4580000, 2630000, 17],
              ['Hungary', 10040000, 2940000, -1, 16],
              ['Iceland', 200000, 198000, -1, 20],
              ['India', 1040000000, 36970000, -1, 43],
              ['Indonesia', 234890000, 12860000, -1, 24],
              ['Iran', 68270000, 420000, -1, 8],
              ['Iraq', 24680000, 12500, -1, 1],
              ['Ireland', 3920000, 1810000, -1, 22],
              ['Islas_Malvinas_(Falkland_Islands)', 2967, -1, -1, 2],
              ['Isle_of_Man', 74261, -1, -1, -1],
              ['Israel', 6110000, 3130000, 976000, 21],
              ['Italy', 57990000, 25530000, 15700000, 93],
              ['Jamaica', 2690000, 100000, -1, 21],
              ['Japan', 127210000, 78050000, 36580000, 73],
              ['Jersey', 90156, -1, -1, -1],
              ['Jordan', 5460000, 212000, -1, 5],
              ['Kazakhstan', 16760000, 100000, -1, 10],
              ['Kenya', 31630000, 500000, -1, 65],
              ['Kiribati', 98549, 1000, -1, 1],
              ['Kuwait', 2180000, 200000, -1, 3],
              ['Kyrgyzstan', 4890000, 51600, -1, -1],
              ['Laos', 5920000, 10000, -1, 1],
              ['Latvia', 2340000, 455000, -1, 41],
              ['Lebanon', 3720000, 300000, -1, 22],
              ['Lesotho', 1860000, 5000, -1, 1],
              ['Liberia', 3310000, 500, -1, 2],
              ['Libya', 5490000, 20000, -1, 1],
              ['Liechtenstein', 3314, -1, -1, -1],
              ['Lithuania', 3590000, 480000, -1, 32],
              ['Luxembourg', 454157, 100000, -1, 8],
              ['Macau', 469903, 101000, -1, 1],
              ['Macedonia', 2060000, 100000, -1, 6],
              ['Madagascar', 16970000, 35000, -1, 2],
              ['Malawi', 11650000, 35000, -1, 7],
              ['Malaysia', 23090000, 10040000, -1, 7],
              ['Maldives', 329684, 6000, -1, 1],
              ['Mali', 11620000, 30000, -1, 13],
              ['Malta', 400420, 59000, -1, 6],
              ['Marshall_Islands', 56429, 900, -1, 1],
              ['Martinique', 425966, 5000, -1, 2],
              ['Mauritania', 2910000, 7500, -1, 5],
              ['Mauritius', 1210000, 158000, -1, 2],
              ['Mexico', 104900000, 13880000, -1, 51],
              ['Micronesia', 108143, 2000, -1, 1],
              ['Moldova', 4430000, 15000, -1, 2],
              ['Monaco', 32130, -1, -1, 2],
              ['Mongolia', 2710000, 40000, -1, 5],
              ['Montserrat', 8995, -1, -1, 17],
              ['Morocco', 31680000, 400000, -1, 8],
              ['Mozambique', 17470000, 22500, -1, 11],
              ['Namibia', 1920000, 45000, -1, 2],
              ['Nauru', 12570, -1, -1, 1],
              ['Nepal', 26460000, 60000, -1, 6],
              ['Netherlands_Antilles', 216226, 2000, -1, 6],
              ['New_Caledonia', 210798, 24000, -1, 1],
              ['New_Zealand', 3950000, 2340000, -1, 36],
              ['Nicaragua', 5120000, 20000, -1, 3],
              ['Niger', 10050000, 12000, -1, 1],
              ['Nigeria', 133880000, 100000, -1, 11],
              ['Niue', 2145, -1, -1, 1],
              ['Norfolk_Island', 1853, -1, -1, 2],
              ['North_Korea', 22460000, -1, -1, 1],
              ['Northern_Mariana_Islar', 80006, -1, -1, 1],
              ['Norway', 4540000, 3030000, -1, 13],
              ['Oman', 2800000, 120000, -1, 1],
              ['Pakistan', 150690000, 1200000, -1, 30],
              ['Palau', 19717, -1, -1, 1],
              ['Panama', 2960000, 45000, -1, 6],
              ['Papua_New_Guinea', 5290000, 135000, -1, 3],
              ['Paraguay', 6030000, 20000, -1, 4],
              ['Peru', 28400000, 4570000, -1, 10],
              ['Philippines', 84610000, 5960000, -1, 33],
              ['Pitcairn_Islands', 47, -1, -1, -1],
              ['Poland', 38620000, 10400000, -1, 19],
              ['Portugal', 10100000, 6090000, -1, 16],
              ['Puerto_Rico', 3880000, 600000, -1, 76],
              ['Qatar', 817052, 75000, -1, 1],
              ['Reunion', 755171, 10000, -1, 1],
              ['Romania', 22270000, 4940000, -1, 38],
              ['Russia', 144520000, 21230000, -1, 35],
              ['Rwanda', 7810000, 20000, -1, 2],
              ['Samoa', 178173, 3000, -1, 2],
              ['San_Marino', 28119, -1, -1, 2],
              ['Sao_Tome_and_Principe', 175883, 9000, -1, 2],
              ['Saudi_Arabia', 24290000, 2540000, -1, 42],
              ['Senegal', 10580000, 100000, -1, 1],
              ['Seychelles', 80469, 9000, -1, 1],
              ['Sierra_Leone', 5730000, 20000, -1, 1],
              ['Singapore', 4600000, 2750000, 956000, 9],
              ['Slovakia', 5430000, 1610000, -1, 6],
              ['Slovenia', 1930000, 930000, -1, 11],
              ['Solomon_Islands', 509190, 8400, -1, 1],
              ['Somalia', 8020000, 200, -1, 3],
              ['South_Africa', 43600000, 4780000, -1, 150],
              ['South_Korea', 42760000, 31670000, -1, 11],
              ['Spain', 40210000, 13440000, 8210000, 56],
              ['Sri_Lanka', 19740000, 121500, -1, 5],
              ['St._Kitts_and_Nevis', 38763, 2000, -1, 16],
              ['St._Lucia', 162157, 3000, -1, 15],
              ['St._Vincent_and_the_Grenadines', 116812, 3500, -1, 15],
              ['Sudan', 38110000, 56000, -1, 2],
              ['Suriname', 435449, 14500, -1, 2],
              ['Svalbard', 2811, -1, -1, -1],
              ['Swaziland', 1160000, 14000, -1, 6],
              ['Sweden', 8870000, 6120000, 4640000, 29],
              ['Switzerland', 7310000, 4600000, 3310000, 44],
              ['Syria', 17580000, 60000, -1, 1],
              ['Taiwan', 22600000, 9520000, 5000000, 8],
              ['Tajilistan', 6860000, 5000, -1, 4],
              ['Tanzania', 35920000, 300000, -1, 6],
              ['Thailand', 64260000, 7570000, -1, 15],
              ['The_Bahamas', 297477, 16900, -1, 19],
              ['The_Netherlands', 16149999, 9790000, 7780000, 52],
              ['Togo', 5420000, 50000, -1, 3],
              ['Tokelau', 1418, -1, -1, 1],
              ['Tonga', 108141, 1000, -1, 2],
              ['Trinidad_and_Tobago', 1100000, 120000, -1, 17],
              ['Tunisia', 9920000, 400000, -1, 1],
              ['Turkey', 68100000, 7270000, -1, 50],
              ['Turkmenistan', 4770000, 2000, -1, -1],
              ['Turks_and_Caicos', 19350, -1, -1, 14],
              ['Tuvalu', 11305, -1, -1, 1],
              ['Uganda', 25630000, 60000, -1, 2],
              ['Ukraine', 48050000, 5278100, -1, 260],
              ['United_Arab_Emirates', 2480000, 900000, -1, 1],
              ['United_Kingdom', 60090000, 33110000, 22550000, 400],
              ['United_States', 290340000, 185550000, 139520000, 7000],
              ['Uruguay', 3410000, 600000, -1, 14],
              ['Uzbekistan', 25980000, 100000, -1, 42],
              ['Vanuatu', 199414, 3000, -1, 1],
              ['Venezuela', 24650000, 2310000, -1, 16],
              ['Vietnam', 81620000, 400000, -1, 5],
              ['Virgin_Islands', 124778, 12000, -1, 50],
              ['Wallis_and_Futuna', 15734, -1, -1, 1],
              ['Western_Sahara', 261794, -1, -1, 1],
              ['Yemen', 19340000, 17000, -1, 1],
              ['Zambia', 10300000, 25000, -1, 5],
              ['Zimbabwe', 12570000, 100000, -1, 6]]


def listPrint(stats, title):
    """Prints each of the top ten lists, formatted
    """
    #sorts by first variable
    stats.sort()

    #prints title, column names
    print(title.upper())
    
    #prints divider
    print("-"*78)
    
    print("%-3s %-30s %10s %10s %10s %10s" %
          (" ", " ", " ", " ", "Active", " "))
    print("%-3s %-30s %10s %10s %10s %10s" %
          (" ", "Country", "Population",
           "Users", "Users",  "ISPs" ))

    #prints divider
    print("-"*78)

    #prints top ten list
    for i in range(10):
        #picks last ten items
        sort, c, p, users, aUsers, ISPs = stats[-(i + 1)]

        #replaces underscore with space in name
        c = c.replace("_", " ")

        #prints, adding counter and removing sort
        print("%-3d %-30s %10d %10d %10d %10d" %
              (i + 1, c, p, users, aUsers, ISPs))

    #prints divider
    print("-"*78)
    
    print("\n")




def population():
    """Creates a list sorted by population
    """

    title = "Top Ten Countries by Population"
    
    stats = []

    #adds value to sort by to beginning of line
    for c, p, users, aUsers, ISPs in statistics:
        stats.append([p, c, p, users, aUsers, ISPs])

    #gives new list to other function to print
    listPrint(stats, title)




def users():
    """Creates a list sorted by users (added)
    """

    title = "Top Ten Countries by Users"
    
    stats = []

    #adds users, active users together and adds
    #them to start of line, skipping n/a
    for c, p, users, aUsers, ISPs in statistics:
        if users == -1: #if internet users n/a
            stats.append([0, c, p, users, aUsers, ISPs])
            
        elif aUsers == -1: #if active users n/a
            stats.append([users, c, p, users, aUsers, ISPs])
            
        else:
            stats.append([users + aUsers, c, p, users,
                          aUsers, ISPs])

    #gives new list to other function to print
    listPrint(stats, title)




def ratio():
    """Creates a list sorted by ratio of
       active users to population
    """

    title = "Top Ten Countries by Ratio \
of Active Users to Population"
    
    stats = []

    #finds ratio of active users to population
    #and adds them to start of line, skipping n/a
    for c, p, users, aUsers, ISPs in statistics:
        if aUsers == -1: #if active users n/a
            stats.append([0, c, p, users, aUsers, ISPs])
            
        else:
            stats.append([aUsers/p, c, p, users,
                          aUsers, ISPs])

    #gives new list to other function to print
    listPrint(stats, title)




def overall():
    """Calculates and prints global
       statistics
    """

    print("GLOBAL STATISTICS:\n")
    
    #variables to recieve statistics
    totPop   = 0
    totUsers = 0
    totAct   = 0
    totISP   = 0
    
    for c, p, users, aUsers, ISPs in statistics:
        #adds population to total
        totPop = totPop + p
        
        #adds if not n/a
        if users != -1:
            totUsers = totUsers + users

        if aUsers != -1:
            totAct   = totAct + aUsers

        if ISPs != -1:
            totISP   = totISP + ISPs

    #finds percentages
    perAct = (totAct/totUsers)*100
    perUse = (totUsers/totPop)*100

    #print statistics
    print("%-40s %10d" %
          ("Total world population:", totPop))
    print("%-40s %10d" %
          ("Total internet users:", totUsers))
    print("%-40s %10d" %
          ("Total active users:", totAct))
    print("%-40s %10d" %
          ("Total number of ISPs:", totISP))
    print("%-40s %10.2f%%" %
          ("Active internet users worldwide:",
           perAct))
    print("%-40s %10.2f%%" %
          ("Internet users worldwide:",
           perUse))    

        




def main():
    """Calls top ten functions
    """
    population()
    users()
    ratio()
    overall()


main()