'''
Name: Sajjan Singh Mehta
Recitation Assignment #6
11/14/2007
PHYS 113-002
Recitation Class #2
'''

from visual import *
from random import *


# Runtime constants
m = 1.2               # Mass of the "horseshoe" ring/torus
g = vector(0, -9.8)   # The graviational acceleration as a vector
C = 1                 # Drag coefficient
rho = 1.3             # Density of the air
r = 0.75              # Radius of "horseshoe" ring
width = 0.2           # Thickness of "horseshoe" ring
A = 0.05              # Surface area of the "horseshoe" ring
#A = 4 * pi**2 * width * (r - width)
dt = 0.01             # Time interval for run
score = 0             # Keeps track of the score


# Set up window properties
scene = display(title = 'Horseshoes', width = 640, height = 480, center = (25, 0, 0),
                forward = (0, -0.30, -1), range = (40, 40, 30), x = 0, y = 0)


# The ground as a plane
ground = box(pos = (25, 0), length = 60, height = 0.25, width = 30,
             color = (0.2, 0.8, 0.2))
# Horseshoe object as a torus
horseshoe = ring(pos = vector(0, 0), p = vector(0, 0), 
                 radius = r, thickness = width, axis = (0, 1), 
                 trail = curve(color = color.red),
                 ideal_pos = vector(0, 0), ideal_p = vector(0, 0), 
                 ideal_trail = curve(color = color.blue))
# The target for the horseshoe 
target = cylinder(pos = vector(0, 0), axis = (0, 2, 0), radius = r,
                  color = (0.6, 0.6, 0.6))
# Your player, represented by a cone
player = cone(pos = (0, 0), axis = (0, 5, 0), radius = 2 * r, color = (0.6, 1, 0.6))

# Label showing the initial velocities selcted
info = label(pos = (0, 20, 0), color = color.white, linecolor = color.black,
             height = 11)
# Displays the current score
score_label = label(pos = (50, 25, 0), color = color.white, height = 11,
                    linecolor = color.black, text = 'Score: '+ str(score))
# Displays the result of a game round after a hit or miss
result_label = label(pos = (25, -20, 0), height = 24, linecolor = color.black,
                     color = color.white)

# Displays the help button
help = label(pos = (0, -30, 0), height = 11, linecolor = color.blue,
             color = color.white, font = 'Arial', text = 'Help? (in console)')
help_box = box(pos = vector(0, -29, 1), length = 14, height = 4, width = 0.1,
               color = color.black)


# Set the scene properties
scene.autoscale = 0
scene.userspin = 0


# Allows the user to choose the initial velocity of the horseshoe
# using a ray to indicate the velocity's vector form
def chooseInitialVelocity():
    global info, target, help_box
    posRay = arrow(pos = (0, 0), axis = (0, 0), shaftwidth = 0.2, 
                   fixedwidth = 1, color = color.blue)
    
    while True:
        posRay.axis = scene.mouse.pos
        info.text = 'Initial Velocity (m/s):\nVx = '+ str(scene.mouse.pos.x) +'\nVy = '+ str(scene.mouse.pos.y)
        
        if scene.mouse.clicked:
            e = scene.mouse.getclick()
            if e.pick == target:
                randomizeTarget()
            elif e.pick == help_box:
                showHelp()
            elif e.pos.x > 0 and e.pos.y > 0:
                posRay.visible = 0
                return e.pos.x, e.pos.y
        

# Resets the game values to its initial conditions
def resetGame():
    global horseshoe, target, result_label
    
    # Reset the horseshoe's position and momentum
    horseshoe.pos = horseshoe.ideal_pos = vector(0, 0)
    horseshoe.p = horseshoe.ideal_p = vector(0, 0)
    
    # Is there an easier way to reset/clear the curves than this?
    horseshoe.trail.visible = False
    horseshoe.trail = curve(color = color.red)
    horseshoe.ideal_trail.visible = False
    horseshoe.ideal_trail = curve(color = color.blue)
    
    result_label.visible = False


# Places the target in a random location
def randomizeTarget():
    global target
    target.x = random() * 50
    target.y = random() * 5


# Shows a little animation for when the user hits the target
def flashTarget():
    global target
    
    for i in xrange(10):
        rate(5)
        if i % 2 == 0:
            target.color = color.yellow
        else:
            target.color = color.blue
    target.color = (0.6, 0.6, 0.6)


# Runs the animation of the horseshoe's projectile motion
def runMotion(vx, vy):
    global horseshoe, target
    
    # Set the horseshoe's initial momentum and trails
    horseshoe.p = m * vector(vx, vy)
    horseshoe.ideal_p = m * vector(vx, vy)
    horseshoe.trail.append(horseshoe.pos)
    horseshoe.ideal_trail.append(horseshoe.ideal_pos)
    
    while True:
        rate(75)
        
        # Update the horseshoe's position and trail curves
        horseshoe.pos += dt * horseshoe.p / m
        horseshoe.trail.append(horseshoe.pos)
        horseshoe.ideal_pos += dt * horseshoe.ideal_p / m
        horseshoe.ideal_trail.append(horseshoe.ideal_pos)
        
        # Account for the force of gravity
        horseshoe.p += dt * (m * g)
        horseshoe.ideal_p += dt * (m * g)
        
        # Account for air resistance
        v = horseshoe.p / m
        v_hat = v / mag(v)
        horseshoe.p += dt * (0.5 * C * rho * A * mag(v)**2) * -v_hat
        
        # Return whether the horseshoe landed within range of the target
        dy = r
        if horseshoe.y - target.y > 0:
            dy += target.axis.y
        
        if abs(horseshoe.y - target.y) < dy and abs(horseshoe.x - target.x) < r:
            return True
        elif horseshoe.y < 0:
            return False


# Prompt the user whether to carry on playing or to quit
def promptUserAction():
    global scene, score, help_box

    cont = label(pos = (58, -30, 0), height = 11, linecolor = color.blue,
                 color = color.white, font = 'Arial', text = 'Continue')
    cont_box = box(pos = vector(57, -29, 1), length = 7.5, height = 4, width = 0.1,
                   color = color.black)
    quit = label(pos = (66, -30, 0), height = 11, linecolor = color.blue,
                 color = color.white, font = 'Arial', text = 'Quit')
    quit_box = box(pos = vector(65, -29, 1), length = 4, height = 4, width = 0.1,
                   color = color.black)

    while True:
        if scene.mouse.clicked:
            e = scene.mouse.getclick()
            if e.pick == help_box:
                showHelp()
            elif e.pick == cont_box:
                cont.visible = cont_box.visible = False
                quit.visible = quit_box.visible = False
                return True
            elif e.pick == quit_box:
                cont.visible = cont_box.visible = False
                quit.visible = quit_box.visible = False
                return False

# Show the help popup
def showHelp():
    print '==========\n   Help\n=========='
    print 'The object of the game is to aim the horseshoe and hit the'
    print 'target.   To do so, move your arrow around with your mouse'
    print 'to set the initial velocity of the horsehoe; the vector'
    print 'components of velocity should be visible in the top-left'
    print 'corner of the screen.  If you hit the target, you will receive'
    print 'a point and the target\'s position will re-randomize.  If you'
    print 'miss, a point will be deducted and you will have another turn'
    print 'on the current target.  Clicking the target will randomize'
    print 'its position.  You have the option to continue or quit at the'
    print 'end of each round you play.  Have fun!\n' 


# Perform an initial randomization of the target
randomizeTarget()

while True:
    # Reset the game values and wait for the user to choose the initial velocity
    resetGame()
    vx, vy = chooseInitialVelocity()
    
    # Performs the appropriate actions if the user hits the target or not
    targetHit = runMotion(vx, vy)
    if targetHit:
        flashTarget()
        
        score += 1
        score_label.text = 'Score: '+ str(score)
        
        result_label.color = color.green
        result_label.visible = True
        result_label.text = 'Nice work, you hit the target!'
    else:
        score -= 1
        score_label.text = 'Score: '+ str(score)
        
        result_label.color = color.red
        result_label.visible = True
        result_label.text = 'Bad luck, try again'
    
    if not promptUserAction():
        print 'Final Score: '+ str(score) +'.  Thanks for playing!'
        scene.visible = False
        sys.exit(0)
    else:
        if targetHit:
            randomizeTarget()
