[SPOILER] Solution to Perl 'Expert' Quiz of the Week #26
Jimmy Selgen <spamtrap1-x/claU4MwXpaa/[email protected]> Mon, 25 Oct 2004 11:02:20 +0200
| Newsgroups | gmane.comp.lang.perl.qotw.discuss |
|---|---|
| Message-ID | <[email protected]> |
Hi I usually never go for the expert quiz because it's too time consuming, but this one sounded like fun (and didn't take that long to make :D ) I'm no physics major, so i'm still a bit confused as to the whole kinetic -> velocity calculation, but at least the cart gains speed when falling, and looses speed when climbing :) I used python for the implementation. /Jimmy -------------------------------------- #!/usr/bin/env python ############################################# # File name : rollercoaster.py # Date : Fri Oct 22 04:41:40 2004 # Author : Jimmy Selgen Nielsen # Email : spamtrap1-x/claU4MwXpaa/[email protected] # Description : # Changelog : # Fri Oct 22 04:41:40 2004 : Initial version ############################################# from Tkinter import * import tkMessageBox,tkFileDialog,sys,os,math,time,tkSimpleDialog class rollercoaster: ''' Perl QOTW expert quiz #26 solution Opens file specified in argv[1], or file opened from menu. ''' def __init__(self,args): self.rc_tag = None self.height = 400 self.width = 600 self.gravity = 1 self.mass = 1 self.time_mult = 100 self.debug = 0 self.wnd = None self.init_display() if(len(args)): self.read_rc(args[0]) self.root.mainloop() def init_display(self): ''' Initialize main window ''' self.root = Tk() self.speedvar = StringVar() self.main_frame = Frame(self.root) self.main_frame.pack(fill=BOTH,expand=1) self.run_button = Button(self.main_frame,text="Run",command=self.run) self.canvas = Canvas(self.main_frame,width=self.width,height=self.height) speed_label = Label(self.main_frame,text="Velocity") self.speedometer = Entry(self.main_frame,textvariable=self.speedvar) self.run_button.pack() self.canvas.pack() speed_label.pack() self.speedometer.pack() self.mainmenu = Menu(self.root) self.root.config(menu=self.mainmenu) self.filemenu = Menu(self.mainmenu) self.filemenu.add_command(label="Open",command=self.open_rc_handler) self.filemenu.add_command(label="Run",command=self.run) self.filemenu.add_command(label="Setup",command=self.config_dialog) self.filemenu.add_command(label="Exit",command=self.root.destroy) self.mainmenu.add_cascade(label="File",menu=self.filemenu) def ok_pressed(self): ''' Config dialog callback for OK ''' self.gravity = float(self.grav_str.get()) self.mass = float(self.mass_str.get()) self.time_mult = float(self.time_mult_str.get()) self.debug = self.dbg_var.get() print "new settings, gravity = %02f, mass = %02f, time_mult = %02f" % (self.gravity,self.mass,self.time_mult) self.wnd.destroy() self.wnd = None def cancel_pressed(self): ''' Config dialog callback for cancel ''' self.wnd.destroy() self.wnd = None def config_dialog(self): ''' Configuration dialog for setting mass, gravity, time multiplier and turning debugging on/off ''' if(self.wnd==None): self.wnd = Toplevel() self.frm = Frame(self.wnd) self.grav_str = StringVar() self.mass_str = StringVar() self.time_mult_str = StringVar() self.dbg_var = IntVar() self.grav_str.set(self.gravity) self.mass_str.set(self.mass) self.time_mult_str.set(self.time_mult) self.dbg_var.set(self.debug) grav_label = Label(self.frm,text="Gravity") self.grav_entry = Entry(self.frm,textvariable=self.grav_str) mass_label = Label(self.frm,text="Mass") self.mass_entry = Entry(self.frm,textvariable=self.mass_str) delay_label = Label(self.frm,text="Delay multiplier (dist/v * mult)") self.time_mult_entry = Entry(self.frm,textvariable=self.time_mult_str) debug_btn = Checkbutton(self.frm,text="Debug",variable=self.dbg_var) ok_btn = Button(self.frm,text="OK",command=self.ok_pressed) cancel_btn = Button(self.frm,text="Cancel",command=self.cancel_pressed) grav_label.pack() self.grav_entry.pack() mass_label.pack() self.mass_entry.pack() delay_label.pack() self.time_mult_entry.pack() debug_btn.pack() ok_btn.pack() cancel_btn.pack() self.frm.pack() def draw_cart(self,coord): ''' Draws the "cart" from the given coordinate set ''' #print "draw_cart, coord = ",coord self.bbox = [coord[0]-2,self.height - (coord[1]-2),coord[0]+2,self.height - (coord[1]+2)] if(self.cart != None): self.canvas.coords(self.cart,*self.bbox) else: self.cart = self.canvas.create_oval(self.bbox,fill="red") def draw_rc(self): ''' Draws the rollercoaster from the current coordinate set ''' self.coords = [] self.cart = None self.canvas.delete('all') for p in range(len(self.rc)) : if(p < len(self.rc)-1): point = self.rc[p].rstrip('\n').split(' ') point[0] = float(point[0]) point[1] = float(point[1]) self.coords.append(point) if(self.rc_tag != None): self.canvas.delete(self.rc_tag) self.rc_tag = [] for p in range(len(self.coords)): if p < len(self.coords)-1: co = [self.coords[p][0],self.height - self.coords[p][1],self.coords[p+1][0],self.height - self.coords[p+1][1]] self.rc_tag.append(self.canvas.create_line(co)) self.canvas.pack(fill=BOTH) def calc_vel(self, curr_state, next_point): ''' Calculates new speed and direction for cart for a given point curr_state : current state of cart next_point : the current point of the cart returns : new state with updated cart speed,direction and time to travel distance ''' new_state = dict() dist = 0 nv = 0 co = self.coords[curr_state['coord']] nco = self.coords[next_point] new_state = curr_state #calculate vertical and horizontal "grid" distance travelled dist = co[0] - nco[0] nh = co[1] - nco[1] dist_travelled = math.sqrt(dist**2 + nh**2) if(self.debug): print "dist :",dist," height :",nh," dist_travelled :",dist_travelled pa = (curr_state['m'] * curr_state['g'] * ((nh/2)/10)) if(pa > 0): nv = math.sqrt(pa) else: nv = -(math.sqrt(-pa)) if(self.debug): print "nv = ",nv new_state['v'] += nv #Change cart direction if velocity is less than zero if(new_state['v']<0): new_state['direction'] = 0 - new_state['direction'] #change negative velocity to positive velocity going the other way :) new_state['v'] = -new_state['v'] if(self.debug): print "new v :",new_state['v'] new_state['coord'] = next_point #Set time to travel distance at new velocity new_state['t'] = dist_travelled / (new_state['v']+1) if(self.debug): print "new t = ",new_state['t'] return(new_state) def read_rc(self,fname): ''' Read a .rc file from disk ''' try: f = open(fname) self.rc = f.readlines() if(self.debug): print "read %d lines from %s" % (len(self.rc),fname) except: tkMessageBox.showerror('Error opening file','Error opening file "'+fname+'"') self.rc = None self.draw_rc() def open_rc_handler(self): ''' Menu callback for "File->Open" ''' fname = tkFileDialog.askopenfilename(filetypes=[(".rc", ".rc")], title="Select the file") if(fname != None): self.read_rc(fname) def tick(self): ''' "Mainloop" of the simulation. Callback to the windowmanager for every iteration of the simulation ''' self.curr_state = self.calc_vel(self.curr_state,self.curr_state['coord']+self.curr_state['direction']) self.draw_cart(self.coords[self.curr_state['coord']]) self.speedvar.set(round(self.curr_state['v'],2)) nco = self.curr_state['coord'] + self.curr_state['direction'] if(self.curr_state['v'] != 0 and nco >= 0 and nco < len(self.coords)): self.root.after(int(self.curr_state['t']*self.time_mult),self.tick) else: return(0) def run(self): ''' Initialize and start mainloop of simulation ''' self.curr_state = { 'coord' : 0, #Index into rollercoaster coordinates 'v':0, #Cart Velocity in the current direction 'm':self.mass, #Mass 'g':self.gravity, #Gravity 't':0, #Time to travel distance 'direction':1 #Direction of cart, 1 = left to right, -1 = right to left } self.calc_vel(self.curr_state,1) self.root.after(int(self.curr_state['t']*self.time_mult),self.tick) if __name__ == '__main__' : rc = rollercoaster(sys.argv[1:])