Sunday, June 16, 2013

Python Game: Asteroids (Rice Rocks)

The due time for this project submission is over so I can post this here. With this final mini project, An Introduction to Interactive Programming in Python class is complete. It has been a wonderful experience! You can play the game here:

__author__ = 'RK Kuppala'

# program template for Spaceship
import simplegui
import math
import random

# globals for user interface
WIDTH = 800
HEIGHT = 600
score = 0
lives = 3
time = 0
wrap = 20
started = False
alive = True
rock_group = set([])
class ImageInfo:
    def __init__(self, center, size, radius = 0, lifespan = None, animated = False):
        self.center = center
        self.size = size
        self.radius = radius
        if lifespan:
            self.lifespan = lifespan
        else:
            self.lifespan = float('inf')
        self.animated = animated

    def get_center(self):
        return self.center

    def get_size(self):
        return self.size

    def get_radius(self):
        return self.radius

    def get_lifespan(self):
        return self.lifespan

    def get_animated(self):
        return self.animated


# art assets created by Kim Lathrop, may be freely re-used in non-commercial projects, please credit Kim

# debris images - debris1_brown.png, debris2_brown.png, debris3_brown.png, debris4_brown.png
#                 debris1_blue.png, debris2_blue.png, debris3_blue.png, debris4_blue.png, debris_blend.png
debris_info = ImageInfo([320, 240], [640, 480])
debris_image = simplegui.load_image("http://commondatastorage.googleapis.com/codeskulptor-assets/lathrop/debris2_blue.png")

# nebula images - nebula_brown.png, nebula_blue.png
nebula_info = ImageInfo([400, 300], [800, 600])
nebula_image = simplegui.load_image("http://commondatastorage.googleapis.com/codeskulptor-assets/lathrop/nebula_blue.png")

# splash image
splash_info = ImageInfo([200, 150], [400, 300])
splash_image = simplegui.load_image("http://commondatastorage.googleapis.com/codeskulptor-assets/lathrop/splash.png")

# ship image
ship_info = ImageInfo([45, 45], [90, 90], 35)
ship_image = simplegui.load_image("http://commondatastorage.googleapis.com/codeskulptor-assets/lathrop/double_ship.png")

# missile image - shot1.png, shot2.png, shot3.png
missile_info = ImageInfo([5,5], [10, 10], 3, 50)
missile_image = simplegui.load_image("http://commondatastorage.googleapis.com/codeskulptor-assets/lathrop/shot2.png")

# asteroid images - asteroid_blue.png, asteroid_brown.png, asteroid_blend.png
asteroid_info = ImageInfo([45, 45], [90, 90], 40)
asteroid_image = simplegui.load_image("http://commondatastorage.googleapis.com/codeskulptor-assets/lathrop/asteroid_blue.png")

# animated explosion - explosion_orange.png, explosion_blue.png, explosion_blue2.png, explosion_alpha.png
explosion_info = ImageInfo([64, 64], [128, 128], 17, 24, True)
explosion_image = simplegui.load_image("http://commondatastorage.googleapis.com/codeskulptor-assets/lathrop/explosion_alpha.png")

# sound assets purchased from sounddogs.com, please do not redistribute
soundtrack = simplegui.load_sound("http://commondatastorage.googleapis.com/codeskulptor-assets/sounddogs/soundtrack.mp3")
missile_sound = simplegui.load_sound("http://commondatastorage.googleapis.com/codeskulptor-assets/sounddogs/missile.mp3")
missile_sound.set_volume(.4)
ship_thrust_sound = simplegui.load_sound("http://commondatastorage.googleapis.com/codeskulptor-assets/sounddogs/thrust.mp3")
ship_thrust_sound.set_volume(.4)
explosion_sound = simplegui.load_sound("http://commondatastorage.googleapis.com/codeskulptor-assets/sounddogs/explosion.mp3")
explosion_sound.set_volume(.5)

# helper functions to handle transformations
def angle_to_vector(ang):
    return [math.cos(ang), math.sin(ang)]

def dist(p,q):
    return math.sqrt((p[0] - q[0]) ** 2+(p[1] - q[1]) ** 2)


# Ship class
class Ship:
    def __init__(self, pos, vel, angle, image, info):
        self.pos = [pos[0],pos[1]]
        self.vel = [vel[0],vel[1]]
        self.thrust = False
        self.angle = angle
        self.angle_vel = 0
        self.image = image
        self.image_center = info.get_center()
        self.image_size = info.get_size()
        self.radius = info.get_radius()

    def draw(self,canvas):
        if alive:
            if self.thrust:
                self.image_center = (135, 45)
            else:
                self.image_center = (45, 45)
        canvas.draw_image(ship_image, self.image_center, self.image_size, self.pos, self.image_size, self.angle)
        if self.pos[0]  WIDTH - self.radius:
            canvas.draw_image(ship_image, self.image_center, self.image_size,
                              [self.pos[0]-WIDTH,self.pos[1]], self.image_size, self.angle)
        if self.pos[1] < self.radius:
            canvas.draw_image(ship_image, self.image_center, self.image_size,
                              [self.pos[0],self.pos[1]+HEIGHT], self.image_size, self.angle)
        if self.pos[1] > HEIGHT - self.radius:
            canvas.draw_image(ship_image, self.image_center, self.image_size,
                              [self.pos[0],self.pos[1]-HEIGHT], self.image_size, self.angle)

    def update(self):
        if alive:
            self.pos[0] += self.vel[0]
            self.pos[0] = (self.pos[0] % WIDTH)
            self.pos[1] += self.vel[1]
            self.pos[1] = (self.pos[1] % HEIGHT)
            self.vel[0] *= 0.97
            self.vel[1] *= 0.97
            self.angle += self.angle_vel
            if self.thrust:
                self.vel[0] += 0.3 * (angle_to_vector(self.angle)[0])
                self.vel[1] += 0.3 * (angle_to_vector(self.angle) [1])

    def angleincrement(self):
        self.angle_vel += 0.08

    def angledecrement(self):
        self.angle_vel -= 0.08

    def thrusters(self):
        self.thrust = not self.thrust
        if self.thrust and started and alive:
            ship_thrust_sound.play()
        else:
            ship_thrust_sound.pause()

    def shoot(self):
        global missile_group
        missile_timer.start()
        if alive and len(missile_group) < 2:
            missile_group.add(Sprite([self.pos[0]+self.radius*angle_to_vector(self.angle)[0], self.pos[1]+self.radius*angle_to_vector(self.angle)[1]],
                                     [11*angle_to_vector(self.angle)[0] + 0.4*self.vel[0], 11*angle_to_vector(self.angle)[1] + 0.4*self.vel[1]],
                                     0, 0, missile_image, missile_info, missile_sound))

    def respawn(self):
        global alive, started
        if lives > 0:
            self.thrust = False
            alive = True
            self.vel[0] = 0
            self.vel[1] = 0
            respawn_timer.stop()
        else:
            started = False
            soundtrack.rewind()
            ship_thrust_sound.rewind()

# Sprite class
class Sprite:
    def __init__(self, pos, vel, ang, ang_vel, image, info, sound = None):
        self.pos = [pos[0],pos[1]]
        self.vel = [vel[0],vel[1]]
        self.angle = ang
        self.angle_vel = ang_vel
        self.image = image
        self.image_center = info.get_center()
        self.image_size = info.get_size()
        self.radius = info.get_radius()
        self.lifespan = info.get_lifespan()
        self.animated = info.get_animated()
        self.age = 0
        if sound:
            sound.rewind()
            sound.play()

    def draw(self, canvas):
        if self.animated:
            canvas.draw_image(self.image, [self.image_center[0] + self.image_size[0] * self.age, self.image_center[1]],
                              self.image_size, self.pos, self.image_size, self.angle)
        else:
            canvas.draw_image(self.image, self.image_center, self.image_size, self.pos, self.image_size, self.angle)
            if self.pos[0] < self.radius:
                canvas.draw_image(self.image, self.image_center, self.image_size,
                                  [self.pos[0]+ WIDTH,self.pos[1]], self.image_size, self.angle)
            if self.pos[0] > WIDTH - self.radius:
                canvas.draw_image(self.image, self.image_center, self.image_size,
                                  [self.pos[0]- WIDTH,self.pos[1]], self.image_size, self.angle)
            if self.pos[1] < self.radius:
                canvas.draw_image(self.image, self.image_center, self.image_size,
                                  [self.pos[0],self.pos[1]+HEIGHT], self.image_size, self.angle)
            if self.pos[1] > HEIGHT - self.radius:
                canvas.draw_image(self.image, self.image_center, self.image_size,
                                  [self.pos[0],self.pos[1]- HEIGHT], self.image_size, self.angle)

    def update(self):
        global wrap
        self.pos[0] += self.vel[0]
        self.pos[0] = (self.pos[0] % WIDTH)
        self.pos[1] += self.vel[1]
        self.pos[1] = (self.pos[1] % HEIGHT)
        self.angle += self.angle_vel
        self.age += 1

    def check_lifespan(self):
        return self.age < self.lifespan

    def collide(self, other_object):
        if dist(self.pos,other_object.pos) < (self.radius + other_object.radius):
            return True


def draw(canvas):
    global time, lives, score, timer, started, alive, rock_group

    # animiate background
    time += 1
    center = debris_info.get_center()
    size = debris_info.get_size()
    wtime = (time / 8) % center[0]
    canvas.draw_image(nebula_image, nebula_info.get_center(), nebula_info.get_size(), [WIDTH/2, HEIGHT/2], [WIDTH, HEIGHT])
    canvas.draw_image(debris_image, [center[0]-wtime, center[1]], [size[0]-2*wtime, size[1]],
                                [WIDTH/2+1.25*wtime, HEIGHT/2], [WIDTH-2.5*wtime, HEIGHT])
    canvas.draw_image(debris_image, [size[0]-wtime, center[1]], [2*wtime, size[1]],
                                [1.25*wtime, HEIGHT/2], [2.5*wtime, HEIGHT])

    if not started:
        canvas.draw_image(splash_image, splash_info.get_center(),
                          splash_info.get_size(), [WIDTH/2, HEIGHT/2],
                          splash_info.get_size())

    # draw ship and sprites
    elif started:
        my_ship.draw(canvas)
        process_sprite_group(rock_group, canvas)
        process_sprite_group(missile_group, canvas)
        process_sprite_group(explosion_group, canvas)
        canvas.draw_text("Lives: " + str(lives), [50,50], 20, "White")
        canvas.draw_text("Score: " + str(score), [WIDTH-150,50], 20, "White")
        # update ship and sprites
        my_ship.update()
        if group_collide(rock_group, my_ship) > 0:
            alive = False
            lives -= 1
            explosion_sound.play()
            respawn_timer.start()
        if group_group_collide(missile_group, rock_group) > 0:
            score += 1
            explosion_sound.rewind()
            explosion_sound.play()

def keydown(key):
    if key == simplegui.KEY_MAP['right']:
        my_ship.angleincrement()
    elif key == simplegui.KEY_MAP['left']:
        my_ship.angledecrement()
    elif key == simplegui.KEY_MAP['up']:
        my_ship.thrusters()
    elif key == simplegui.KEY_MAP['space']:
        my_ship.shoot()

def keyup(key):
    if key == simplegui.KEY_MAP['right']:
        my_ship.angledecrement()
    elif key == simplegui.KEY_MAP['left']:
        my_ship.angleincrement()
    elif key == simplegui.KEY_MAP['up']:
        my_ship.thrusters()
    elif key == simplegui.KEY_MAP['space']:
        missile_timer.stop()

# timer handler that spawns a rock
def rock_spawner():
    global rock_group
    random_place = [random.randrange(0, WIDTH), random.randrange(0, HEIGHT)]
    while dist(random_place,my_ship.pos) < 150:
        random_place = [random.randrange(0, WIDTH), random.randrange(0, HEIGHT)]
    random_velocity = [random.choice([-1, -2, 1, 2]), random.choice([-1, -2, 1, 2])]
    random_angle = random.randrange(0, 7)
    random_anglevel = (random.randrange(-30,30) / 1000)
    if len(rock_group) < 5:
        rock_group.add((Sprite(random_place, random_velocity, random_angle, random_anglevel, asteroid_image, asteroid_info)))

def process_sprite_group(a_set, canvas):
    for sprite in a_set:
        sprite.draw(canvas)
    for sprite in a_set:
        sprite.update()
    for sprite in a_set:
        if not sprite.check_lifespan():
            a_set.remove(sprite)


def group_collide(group, other_object):
    collide = 0
    for an_object in group:
        if Sprite.collide(an_object, other_object):
             explosion_group.add(Sprite(an_object.pos, (0,0), 0, 0, explosion_image, explosion_info, explosion_sound))
             if other_object.radius > 10:
                 explosion_group.add(Sprite(other_object.pos, (0,0), 0, 0, explosion_image, explosion_info, explosion_sound))
             group.remove(an_object)
             collide += 1
    return collide

def group_group_collide(group1, group2):
    collide = 0
    for an_object in group1:
        if group_collide(group2, an_object) > 0:
            group1.remove(an_object)
            collide += 1
    return collide

def click(pos):
    global started, lives
    center = [WIDTH / 2, HEIGHT / 2]
    size = splash_info.get_size()
    inwidth = (center[0] - size[0] / 2) < pos[0] < (center[0] + size[0] / 2)
    inheight = (center[1] - size[1] / 2) < pos[1] < (center[1] + size[1] / 2)
    if (not started) and inwidth and inheight:
        reset()
        soundtrack.play()
        started = True


def reset():
    global my_ship, rock_group, missile_group, lives, score
    my_ship = Ship([WIDTH / 2, HEIGHT / 3], [0, 0], 0, ship_image, ship_info)
    rock_group = set()
    missile_group = set()
    explosion_group = set()
    score = 0
    lives = 3
    timer.start()

def missile():
    my_ship.shoot()

# initialize frame
frame = simplegui.create_frame("Asteroids", WIDTH, HEIGHT)

# initialize ship and two sprites
my_ship = Ship([WIDTH / 2, HEIGHT / 2], [0, 0], 0, ship_image, ship_info)
a_rock = Sprite([WIDTH / 3, HEIGHT / 3], [1, 1], 0, 0, asteroid_image, asteroid_info)
a_missile = Sprite([2 * WIDTH / 3, 2 * HEIGHT / 3], [-1,1], 0, 0, missile_image, missile_info, missile_sound)

# register handlers
frame.set_draw_handler(draw)
frame.set_mouseclick_handler(click)
timer = simplegui.create_timer(500.0, rock_spawner)
frame.set_keydown_handler(keydown)
frame.set_keyup_handler(keyup)    

my_ship = Ship([WIDTH / 2, HEIGHT / 3], [0, 0], 0, ship_image, ship_info)
missile_group = set()
explosion_group = set()
missile_timer = simplegui.create_timer(700.0, missile)
respawn_timer = simplegui.create_timer(700.0, my_ship.respawn)

# get things rolling
timer.start()
frame.start()

Thursday, June 6, 2013

Playing with text file data in Python: Building SQL delete statements from unstructured data

Yesterday I played around with excel files to build SQL update statements and today I got an opportunity do similar stuff, but this time I got the text file that had column names in the first line and values in following lines separated by spaces. The goal is delete a bunch of records from a table based on the eventids etc. It looks like this:



First, you need to open the file to read it. The rb argument is to open the file for reading in binary mode in windows.

myFile = open("D:\Scripts\deldata.txt", "rb")

I initially tried to find the number of lines in the text file thinking this might be useful while iterating through the lines and when trying to make a list out of these . I know, you could just open the text file in notepad++ and look at the end of the document, but where is the fun in that? So this is what I came up with.

myFile = open("D:\Scripts\deldata.txt", 'rb')
i = 0
for line in enumerate(myFile):
    i += 1
print i

I know that totally sucks. May be there a more elegant way?

myFile = open("D:\Scripts\deldata.txt", "rb")
numLines = sum(1 for line in open myFile)
print numLines

It turns out, you don't really need it. You can just loop through each line and manipulate the lines as needed. When I printed the lines, there were lots of white spaces and tabs in the resulting strings. After browsing through a lot of stackoverflow questions and reading documentation and unsuccessfully trying to use regular expressions, I found an easier way to deal with the white spaces. Just use string.split() method and turn the string into a list.

__author__ = 'RK'
myFile = open("D:\Scripts\deldata.txt", "rb")

for line in myFile:
    line = line.split() #remove white spaces and tabs
    for value in line:
        print ("DELETE FROM REGISTR WHERE eventid = '%s' AND subeventid = '%s' AND personid = '%s' 
                AND RegNo = '%s'" %(line[0], line[1], line[2], line[3]))
    





Wednesday, June 5, 2013

Manipulating Excel data with Python: Building SQL statements from data in excel

I got an excel doc with a couple of sheets from our dev team. Each sheet has two columns from one of our database tables. Lets call these columns CurrentID, LegacyID. I need to update the LegacyID based on CurrentID. Each sheet has more than 2000 values and it is pointless trying to build the SQL statements manually. Its pretty easy to get this done using TSQL. Just import this data into a temporary table and build update statements using TSQL. Since I have been trying to learn Python, I thought I'd give it a try. I used xlrd module. Check the documentation here. Below code writes the update statements to a text file from sheet1.

import xlrd

book = xlrd.open_workbook('D:\Scripts\UpdateID01.xls')
sheet = book.sheet_by_index(0)

myList = []
for i in range(1,sheet.nrows):
   myList.append(sheet.row_values(i))

#print myList
outFile = open('D:\Scripts\update.txt', 'wb')

for i in myList:
    outFile.write("\nUPDATE MyTable SET LegacyID = '%s' WHERE CurrentID = '%s'" %( int(i[1]), str(i[0])))

The second sheet had white spaces at the end of CurrentID values and the update statements will fail if they are not removed. To deal with it, use str.replace method.

import xlrd

book = xlrd.open_workbook('D:\Scripts\UpdateID01.xls')
sheet = book.sheet_by_index(1)

myList = []
for i in range(1,sheet.nrows):
   myList.append(sheet.row_values(i))

#print myList
outFile = open('D:\Scripts\updatemyesi.txt', 'wb')

for i in myList:
    i[0] = str(i[0]).replace('  ', '')
    outFile.write("\nUPDATE MyTable SET LegacyID = 0 WHERE CurrentID = '%s'" %(str(i[0])))