Author: Reiner Herrmann <reiner@reiner-h.de>
Description: Port to Python 3
Bug-Debian: https://bugs.debian.org/912509

--- a/src/Main.py
+++ b/src/Main.py
@@ -68,7 +68,7 @@
 
 #-------------------------------------------------------------------------------------------
 # screen 
-if os.sys.platform <> 'darwin':
+if os.sys.platform != 'darwin':
     pygame.display.set_icon(pygame.image.load('levels/images/icon64x64.png'))
 
 #-------------------------------------------------------------------------------------------
--- a/src/Config.py
+++ b/src/Config.py
@@ -68,8 +68,8 @@
         li = k.level.isBonus() and self.stage-1 or level-1
 
         # stats
-        self['totalTime']       += k.level.time/1000
-        self['stageTime'][si]   += k.level.time/1000
+        self['totalTime']       += k.level.time//1000
+        self['stageTime'][si]   += k.level.time//1000
         self['totalSolved'][si] += 1
 
         # failsafe
@@ -77,16 +77,16 @@
             self['scores'].append([])
         
         while len(self['scores'][si]) < level:
-            self['scores'][si].append({'time': sys.maxint, 'score': sys.maxint})
+            self['scores'][si].append({'time': sys.maxsize, 'score': sys.maxsize})
                     
-        if self['scores'][si][li]['time'] == 0: self['scores'][si][li]['time']  = sys.maxint
+        if self['scores'][si][li]['time'] == 0: self['scores'][si][li]['time']  = sys.maxsize
 
         # first time level solved 
-        if self['scores'][si][level-1]['score'] == sys.maxint:
+        if self['scores'][si][level-1]['score'] == sys.maxsize:
             self.makeLevelAvailable()
 
         # update scores
-        secs = k.level.time/1000
+        secs = k.level.time//1000
 
         self['scores'][si][li]['time']  = min(self['scores'][si][li]['time'], secs)
         self['scores'][si][li]['score'] = min(self['scores'][si][li]['score'], int(k.score))
@@ -103,13 +103,13 @@
     #-------------------------------------------------------------------------------------------
     def abort (self):
         si = k.level.isBonus() and 4 or self.stage-1
-        self['totalTime']     += k.level.time/1000
-        self['stageTime'][si] += k.level.time/1000
+        self['totalTime']     += k.level.time//1000
+        self['stageTime'][si] += k.level.time//1000
         
     #-------------------------------------------------------------------------------------------
     def makeLevelAvailable (self):
         if len(self['scores'][self.stage - 1]) < levels.numLevels:
-        	    self['scores'][self.stage - 1].append({'time': sys.maxint, 'score': sys.maxint})
+        	    self['scores'][self.stage - 1].append({'time': sys.maxsize, 'score': sys.maxsize})
             
     #-------------------------------------------------------------------------------------------
     def apply(self):
@@ -120,7 +120,7 @@
     def getConfigFilePath(self):
         if os.sys.platform == 'darwin':
             return os.path.expanduser('~/Library/Preferences/krank.cfg')
-        elif os.sys.platform == 'linux2':
+        elif os.sys.platform.startswith('linux'):
             return os.path.expanduser('~/.krankcfg')
         else:
             return os.path.join(os.environ['APPDATA'], 'krank.cfg')
@@ -144,7 +144,7 @@
             file = open(config_file, 'w+')
             file.write("# vim: filetype=yaml :\n")
             yaml.dump(self, file)
-        except Exception, e:
+        except Exception as e:
             log(e)
         
     #-------------------------------------------------------------------------------------------
@@ -158,29 +158,29 @@
             if not os.access(config_file, os.R_OK):
                 config_file += '.yaml'
                 file = open(config_file, 'r')
-                self.update(yaml.load(file))
+                self.update(yaml.load(file, Loader=yaml.FullLoader))
             else:
                 file = open(config_file, 'r')
-                self.update(cPickle.load(file))
+                self.update(pickle.load(file))
             
             self.stage = self['stage']
             k.config.fullscreen=self['fullscreen']
             while len(self['scores']) < 4: self['scores'].append([])
             log(self, log='config')
-        except Exception, e:
+        except Exception as e:
             log(e)
         
     #-------------------------------------------------------------------------------------------
     def bestTimeString (self, level=0, stage=0):
         time = self.bestTime(level, stage)
-        if time < sys.maxint/1000:
+        if time < sys.maxsize//1000:
             return k.level.timeString(time)
         return ""
 
     #-------------------------------------------------------------------------------------------
     def bestScoreString (self, level=0, stage=0):
         score = self.bestScore(level, stage)
-        if score < sys.maxint:
+        if score < sys.maxsize:
             return "%d" % score
         return ""
         
@@ -204,7 +204,7 @@
     
     #-------------------------------------------------------------------------------------------
     def setStage (self, stage):
-        if stage <> self.stage:
+        if stage != self.stage:
             k.sound.play('exit')
             self.stage = stage
             self.save()
@@ -217,7 +217,7 @@
         scores = self['scores'][stage-1]
         for index in range(-1, -len(scores), -1):
             score = scores[index]
-            if score['score'] < sys.maxint:
+            if score['score'] < sys.maxsize:
                 return len(scores)+index+1
         return 0
     
@@ -231,6 +231,6 @@
     def numSolvedLevels (self, stage=0):
         if not stage:
             stage = self.stage
-        return len([s for s in self['scores'][stage-1] if s['score'] < sys.maxint])
+        return len([s for s in self['scores'][stage-1] if s['score'] < sys.maxsize])
     
     
--- a/src/Krank.py
+++ b/src/Krank.py
@@ -4,10 +4,10 @@
 #-----------------------------------------------------------------------------------------------
 # imports
 
-import sys, os.path, dircache, math, sets, re, sets, shutil, time, copy, cPickle, pickle, random
+import sys, os.path, math, re, shutil, time, copy, pickle, random
 import pygame
 
-kNEXT_LEVEL, kMENU_LEVEL, kLOAD_LEVEL = range(pygame.USEREVENT, pygame.USEREVENT+3)
+kNEXT_LEVEL, kMENU_LEVEL, kLOAD_LEVEL = list(range(pygame.USEREVENT, pygame.USEREVENT+3))
 
 from Math import *
 
@@ -23,25 +23,25 @@
     if "log" in kwargs:
         if kwargs["log"] not in _log_:
             return
-        print kwargs["log"].rjust(catjust),
+        print(kwargs["log"].rjust(catjust),)
     else:
-        print " ".rjust(catjust),
-    print "[", 
+        print(" ".rjust(catjust),)
+    print("[", )
     mth = sys._getframe(level).f_code.co_name
     try:
         obj = sys._getframe(level).f_locals['self']
         cls = hasattr(obj.__class__, 'className') and obj.__class__.className(obj) or str(obj.__class__.__name__)
-        print cls.ljust(clsjust), mth.ljust(mthjust),
-    except Exception, e:
+        print(cls.ljust(clsjust), mth.ljust(mthjust),)
+    except Exception as e:
         try:
             file = sys._getframe(level).f_globals['__file__']
             moduleName = os.path.join(os.path.split(os.path.dirname(file))[-1], os.path.basename(file))
-            print moduleName.ljust(clsjust), mth.ljust(mthjust),
+            print(moduleName.ljust(clsjust), mth.ljust(mthjust),)
         except:
-            print "???".ljust(clsjust), mth.ljust(mthjust),
-    print "]",
-    for i in args: print i,
-    print ""
+            print("???".ljust(clsjust), mth.ljust(mthjust),)
+    print("]",)
+    for i in args: print(i,)
+    print("")
 
 #-----------------------------------------------------------------------------------------------
 
--- a/src/Level.py
+++ b/src/Level.py
@@ -45,8 +45,8 @@
             file, filename, description = imp.find_module(self.name, levels.__path__)
             levelModule = imp.load_module(self.name, file, filename, description)
             log('level module loaded: %s' % (self.name, ), log='level')
-        except Exception, e:
-            if e.__class__ <> ImportError:
+        except Exception as e:
+            if e.__class__ != ImportError:
                 error(e)
             log(e)
                 
@@ -54,7 +54,7 @@
             try:
                 log('initializing level: %s' % (self.name, ), log='level')
                 levelModule.init()
-            except Exception, e:
+            except Exception as e:
                 error(e)
                 
         k.score = 0
@@ -72,7 +72,7 @@
     #-------------------------------------------------------------------------------------------
     def timeString (self, secs=0):
         if secs == 0: 
-            secs = self.time/1000
+            secs = self.time//1000
         return timeString(secs)
         
     #-------------------------------------------------------------------------------------------
@@ -82,7 +82,7 @@
     #-------------------------------------------------------------------------------------------
     def menu (self):
         pygame.time.set_timer(kMENU_LEVEL, 0)
-        if self.name[:4] <> 'menu': self.name = 'menu'
+        if self.name[:4] != 'menu': self.name = 'menu'
         Level(self.name)
 
     #-------------------------------------------------------------------------------------------
@@ -181,17 +181,17 @@
             if self.time < 700:
                 fac = (1-self.time/700.0)
                 fac = fac*fac
-                fadeYOffset = -(k.world.rect.height/40+42)*fac
-                fadeXOffset = -(k.world.rect.height/30+120)*fac
+                fadeYOffset = -(k.world.rect.height//40+42)*fac
+                fadeXOffset = -(k.world.rect.height//30+120)*fac
             
             timeString = k.input.pause and "Pause" or self.timeString()
             textRect = cockpitText(timeString, 
-                                    (k.world.rect.width/30, k.world.rect.height/40+fadeYOffset), 
+                                    (k.world.rect.width//30, k.world.rect.height//40+fadeYOffset), 
                                     size='small', 
                                     color=(220, 220, 220))
-            if k.config.numSolvedLevels() >= k.level.number and k.config.bestTime(k.level.number) < sys.maxint/1000:
+            if k.config.numSolvedLevels() >= k.level.number and k.config.bestTime(k.level.number) < sys.maxsize//1000:
                 textRect = textRect.union(cockpitText(self.timeString(k.config.bestTime(k.level.number)), 
-                                                       (k.world.rect.width/30+fadeXOffset, k.world.rect.height/40+42), 
+                                                       (k.world.rect.width//30+fadeXOffset, k.world.rect.height//40+42), 
                                                        size='tiny', 
                                                        color=(180, 180, 180)))
     
@@ -201,13 +201,13 @@
             # ----------------------------------------------------------------------------- score
     
             textRect = cockpitText("%d" % k.score, 
-                                     (k.world.rect.width*29/30, k.world.rect.height/40+fadeYOffset), 
+                                     (k.world.rect.width*29//30, k.world.rect.height//40+fadeYOffset), 
                                      size='small', 
                                      align='right', 
                                      color=(220, 220, 220))
-            if k.config.lastSolvedLevel() >= k.level.number and k.config.bestScore(k.level.number) < sys.maxint:
+            if k.config.lastSolvedLevel() >= k.level.number and k.config.bestScore(k.level.number) < sys.maxsize:
                 textRect = textRect.union(cockpitText("%d" % k.config.bestScore(k.level.number), 
-                                                         (k.world.rect.width*29/30-fadeXOffset, k.world.rect.height/40+42), 
+                                                         (k.world.rect.width*29//30-fadeXOffset, k.world.rect.height//40+42), 
                                                          size='tiny', 
                                                          align='right', 
                                                          color=(180, 180, 180)))
@@ -220,7 +220,7 @@
             if k.world.fpsrect:
                 k.screen.blit(k.world.image, k.world.fpsrect, k.world.fpsrect)
             fpsrect = drawText("%03d" % k.clock.get_fps(), 
-                                 (k.world.rect.width/30, k.world.rect.height*28/30), 
+                                 (k.world.rect.width//30, k.world.rect.height*28//30), 
                                  size='small', surface=k.screen, 
                                  align='left', color=(0,0,0))
             k.world.fpsrect = k.world.fpsrect and k.world.fpsrect.union(fpsrect) or fpsrect
--- a/src/Math.py
+++ b/src/Math.py
@@ -25,12 +25,18 @@
         else:
             return vector((self[0]*other, self[1]*other))
 
-    def __div__(self, other):
+    def __truediv__(self, other):
         if other.__class__ == vector:
             return vector((self[0]/other[0], self[1]/other[1]))
         else:
             return vector((self[0]/other, self[1]/other))
 
+    def __floordiv__(self, other):
+        if other.__class__ == vector:
+            return vector((self[0]//other[0], self[1]//other[1]))
+        else:
+            return vector((self[0]//other, self[1]//other))
+
     def __rmul__(self, other):
         return (self*other)
         
@@ -90,7 +96,7 @@
     #-------------------------------------------------------------------------------------------
     def length(self):
         dot = abs(self.dot(self))
-        if dot < sys.maxint:
+        if dot < sys.maxsize:
             return math.sqrt(dot)
         return 0
 
@@ -156,7 +162,7 @@
         
     #-------------------------------------------------------------------------------------------
     def out(self):
-        print self
+        print(self)
         
 #-----------------------------------------------------------------------------------------------
 def pos (x, y):
@@ -289,4 +295,4 @@
     else:
         return a * (1-f) + b * f
 
-    
\ No newline at end of file
+    
--- a/src/Player.py
+++ b/src/Player.py
@@ -63,11 +63,11 @@
                 p = Particle({'pos': op.pos+direction*pos(radius[i],0), 
                               'drag': 0.04, 
                               'player': 2+i, 
-                              'radius': radius[i]/2,
+                              'radius': radius[i]//2,
                               'image': image[i],
                               'sprites': k.player_sprites})
                 k.particles.add(p)
-                length = (i > 0) and (sizes[i]/2+sizes[i-1]/2) or (sizes[i]/2+40/2)
+                length = (i > 0) and (sizes[i]//2+sizes[i-1]//2) or (sizes[i]//2+40//2)
                 k.particles.add(Spring(op, p, length=length, oneWay=1, spring=300.0, damp=20.0))
                 op = p
                 
@@ -87,7 +87,7 @@
             dragFactor *= delta/1000.0
             self.part.vel += -self.part.vel.norm()*length*length*dragFactor
                                    
-        except Exception, e:
+        except Exception as e:
             error(e)
 
-        
\ No newline at end of file
+        
--- a/src/Sprite.py
+++ b/src/Sprite.py
@@ -27,7 +27,7 @@
 
         self.rect = self.image.get_rect()
         self.setPos(pos)
-        if sprites <> None:
+        if sprites != None:
           sprites.add(self)
         else:
           k.sprites.add(self)
@@ -36,7 +36,7 @@
     def setPos (self, pos):
         try:
             self.rect.center = pos
-        except Exception, e:
+        except Exception as e:
             self.rect.center = (clamp(pos.x, 0, k.world.rect.width), clamp(pos.y, 0, k.world.rect.height))
                                 
-        
\ No newline at end of file
+        
--- a/src/World.py
+++ b/src/World.py
@@ -45,7 +45,7 @@
         
     #-------------------------------------------------------------------------------------------
     def setScreen (self, size):
-        if size <> self.rect.size:
+        if size != self.rect.size:
             self.image = None
             k.sound.play('exit')
             k.screen = pygame.display.set_mode(size, self.screen_flags)
@@ -82,7 +82,7 @@
     # fonts
     #-------------------------------------------------------------------------------------------        
     def postInit (self):
-        if os.sys.platform <> 'darwin':
+        if os.sys.platform != 'darwin':
             pygame.display.set_gamma(1.4)
             
         self.forceFactor = 1.0-clamp((1024-self.rect.height)/1800.0, -0.15, 0.4)
@@ -91,17 +91,17 @@
             height = self.rect.height
             try:
                 fontfile = '/usr/share/fonts/truetype/ttf-dejavu/DejaVuSans.ttf'
-                k.font['large']  = pygame.font.Font(fontfile, height/24)
-                k.font['normal'] = pygame.font.Font(fontfile, height/36)
+                k.font['large']  = pygame.font.Font(fontfile, height//24)
+                k.font['normal'] = pygame.font.Font(fontfile, height//36)
                 k.font['small']  = pygame.font.Font(fontfile, 30)
                 k.font['tiny']   = pygame.font.Font(fontfile, 20)
-            except Exception, e:
+            except Exception as e:
                   log(e)
-                  k.font['large']  = pygame.font.SysFont(pygame.font.get_default_font(), height/12)
-                  k.font['normal'] = pygame.font.SysFont(pygame.font.get_default_font(), height/24)
+                  k.font['large']  = pygame.font.SysFont(pygame.font.get_default_font(), height//12)
+                  k.font['normal'] = pygame.font.SysFont(pygame.font.get_default_font(), height//24)
                   k.font['small']  = pygame.font.SysFont(pygame.font.get_default_font(), 40)
                   k.font['tiny']   = pygame.font.SysFont(pygame.font.get_default_font(), 30)    
-        except Exception, e:
+        except Exception as e:
           log(e)
           k.font = None
                         
@@ -125,9 +125,9 @@
             log('[ERROR] image doesn\'t exist', file)
             return
         
-        if self.image and k.level.name[:11] <> 'menu_levels':
-            xtiles, ytiles = self.rect.width/self.tilesize+1, self.rect.height/self.tilesize+1
-            self.fadeTiles = range(xtiles*ytiles)
+        if self.image and k.level.name[:11] != 'menu_levels':
+            xtiles, ytiles = self.rect.width//self.tilesize+1, self.rect.height//self.tilesize+1
+            self.fadeTiles = list(range(xtiles*ytiles))
             random.shuffle(self.fadeTiles)
         self.image = quadMirrorSurface(pygame.image.load(file), self.rect.size).convert()            
 
@@ -155,11 +155,11 @@
     def onFrame (self, delta):
         if len(self.fadeTiles):
             rectlist = []
-            for i in range(min(len(self.fadeTiles), self.rect.width/self.tilesize)):
+            for i in range(min(len(self.fadeTiles), self.rect.width//self.tilesize)):
                 tilesize = self.tilesize
                 tile = self.fadeTiles.pop(0)
-                xtiles, ytiles = self.rect.width/tilesize+1, self.rect.height/tilesize+1
-                rect = pygame.Rect((tile%xtiles)*tilesize, (tile/xtiles)*tilesize, tilesize, tilesize)
+                xtiles, ytiles = self.rect.width//tilesize+1, self.rect.height//tilesize+1
+                rect = pygame.Rect((tile%xtiles)*tilesize, (tile//xtiles)*tilesize, tilesize, tilesize)
                 rect = rect.clip(self.image.get_rect())
                 image = self.image.subsurface(rect)
                 k.screen.blit(image, rect)
--- a/krank
+++ b/krank
@@ -2,4 +2,4 @@
 KRANKPATH=`dirname $0`
 PYTHONPATH=$KRANKPATH:$PYTHONPATH
 export PYTHONPATH
-python $KRANKPATH/src/Main.py
+python3 $KRANKPATH/src/Main.py
--- a/levels/menu_screen.py
+++ b/levels/menu_screen.py
@@ -26,9 +26,9 @@
                              'pos': pos(cx+cx/3, h*7/8)}))
     modes = pygame.display.list_modes(32, pygame.FULLSCREEN)
     modes = [m for m in modes if (m[1] >= 600)]
-    set = sets.Set()
-    for m in modes: set.add(m)
-    modes = list(set)
+    mset = set()
+    for m in modes: mset.add(m)
+    modes = list(mset)
     modes.sort(reverse=True)
     for i in range(len(modes)):
         m = modes[i]
--- a/src/Part.py
+++ b/src/Part.py
@@ -20,7 +20,7 @@
     def __init__ (self, dict={}):
         self.imass = dict.get('imass', 1.0)
         
-        self.pos = vector(dict.get('pos', (k.world.rect.centerx/2, k.world.rect.centery/2)))
+        self.pos = vector(dict.get('pos', (k.world.rect.centerx//2, k.world.rect.centery//2)))
         self.vel = vector(dict.get('vel', (0,0)))
         self.force  = vector(dict.get('force', (0,0)))
         
@@ -79,7 +79,7 @@
       
         self.color = dict.get('color', 'white')
 
-        if not dict.has_key('image'):
+        if 'image' not in dict:
             icon = dict.get('size', 'large') == "large" and "menu" or "menu_small" 
             image = pygame.image.load('levels/images/%s_%s.png' % (icon, self.color))
             dict['image'] = image
@@ -121,7 +121,7 @@
             if self.group:
                 self.sprite.image = pygame.image.load('levels/images/menu_orange.png')
                 for part in k.particles.parts:
-                    if part <> self and hasattr(part, 'group') and part.group == self.group:
+                    if part != self and hasattr(part, 'group') and part.group == self.group:
                         part.sprite.image = pygame.image.load('levels/images/menu_white.png')
      
 #-----------------------------------------------------------------------------------------------
@@ -146,8 +146,8 @@
             dir = vector.withAngle(i*2*math.pi/(self.num)+self.angle, 16+10-1)
             image = pygame.image.load('levels/images/dot20_%s.png' % (self.color,))
             self.dots.append(Sprite(self.pos+dir, image, k.magnet_sprites))
-        self.captured = sets.Set()
-        self.oldCaptured = sets.Set()
+        self.captured = set()
+        self.oldCaptured = set()
                 
         self.actionCounter = 0
         
@@ -188,7 +188,7 @@
                 if self.actionCounter > 3000:
                     self.exploding = True
                     self.actionCounter = 0
-                    colorSet = sets.Set()
+                    colorSet = set()
                     for p in self.captured:
                         p.vel += self.pos.to(p.pos).norm()*1000*k.world.forceFactor
                         p.captured = 0
@@ -199,7 +199,7 @@
                         if not k.level.checkExit():
                             k.sound.play('magnet_action', force=1)
                     else:
-                        self.captured = sets.Set()
+                        self.captured = set()
                         k.sound.play('magnet_action', force=1)
                 elif int((self.actionCounter)/1000) > int((self.actionCounter-delta)/1000):
                     k.sound.play('magnet_start', force=1)
@@ -207,7 +207,7 @@
                 self.actionCounter = 0
 
         self.oldCaptured = self.captured
-        self.captured = sets.Set()
+        self.captured = set()
 
     #-------------------------------------------------------------------------------------------        
     def onFrame (self, delta):
@@ -215,7 +215,7 @@
         self.angle += self.anglefac*math.pi*delta/8000
         for i in range(self.num):
             dir = vector.withAngle(self.angle+i*2*math.pi/(self.num), 16+10)
-            self.dots[i].rect = pygame.Rect(self.pos+dir-vector((self.dots[i].rect.size))/2, self.dots[i].rect.size)
+            self.dots[i].rect = pygame.Rect(self.pos+dir-vector((self.dots[i].rect.size))//2, self.dots[i].rect.size)
             
         if len(self.oldCaptured):
             pointlist = [(clamp(p.pos.x, self.pos.x-31, self.pos.x+31), clamp(p.pos.y, self.pos.y-31, self.pos.y+31)) for p in self.oldCaptured]
@@ -279,7 +279,7 @@
         self.maxLinks = dict.get('maxLinks', 2)
         self.isAnchor = False
         
-        if not dict.has_key('image'):
+        if 'image' not in dict:
             self.color = dict.get('color', 'white')
             image = pygame.image.load('levels/images/dot28_d_%s.png' % (self.color,))
             dict['image'] = image
@@ -308,9 +308,9 @@
         chains.append(self)
         if self.isAnchor: return
         if self.links:
-            if self.links[0] <> source:                
+            if self.links[0] != source:                
                 self.links[0].traverseChain(self, chains)
-            elif len(self.links) > 1 and self.links[1] <> source:
+            elif len(self.links) > 1 and self.links[1] != source:
                 self.links[1].traverseChain(self, chains)
     
     #-------------------------------------------------------------------------------------------    
@@ -350,7 +350,7 @@
             k.sound.play('unlink')
 
             for cp in self.chain:
-                if cp <> self and self in cp.chain:
+                if cp != self and self in cp.chain:
                     cp.chain.remove(self)
                     
             for cp in self.links:
@@ -413,7 +413,7 @@
                     self.actionCounters.pop(key)
                     continue
             
-            if key <> self.color:
+            if key != self.color:
                 if not key in self.links:
                     self.actionCounters.pop(key)
                     continue
@@ -462,7 +462,7 @@
         self.angle += self.anglefac*math.pi*delta/8000
         for i in range(self.maxLinks):
             dir = vector.withAngle(self.angle+i*2*math.pi/(self.maxLinks), 16+10)
-            self.dots[i].rect = pygame.Rect(self.pos+dir-vector((self.dots[i].rect.size))/2, self.dots[i].rect.size)
+            self.dots[i].rect = pygame.Rect(self.pos+dir-vector((self.dots[i].rect.size))//2, self.dots[i].rect.size)
                    
     #-------------------------------------------------------------------------------------------
     def checkComplete (self): 
@@ -487,14 +487,14 @@
         for link in self.links:
             if len([p for p in link.chain if p.isAnchor]) == 2:
                 if self.colorsInChain(link.chain) > 1:
-                    if not self.actionCounters.has_key(link):
+                    if link not in self.actionCounters:
                         self.actionCounters[link] = 1
                         return True
         return False
     
     #-------------------------------------------------------------------------------------------
     def colorsInChain (self, chain):
-        colorSet = sets.Set()
+        colorSet = set()
         for p in chain:
             colorSet.add(p.color)
         return len(colorSet)
@@ -584,7 +584,7 @@
     #-------------------------------------------------------------------------------------------
     def onFrame (self, delta):
         stepSize = clamp(delta, 1, 10)
-        steps = clamp(delta/stepSize, 1, 20)
+        steps = clamp(delta//stepSize, 1, 20)
         stepSize = 1.0*delta/steps
         for i in range(steps):
             k.player.onTick(stepSize)
@@ -636,9 +636,9 @@
             elif part.pos.x > k.world.rect.right:
                 norm = vector((-1,0))
                 
-            if norm <> None:
+            if norm != None:
                 part.pos = pos(clamp(part.pos.x, 0, k.world.rect.width), clamp(part.pos.y, 0, k.world.rect.height))
-                if (norm.dot(norm)*part.imass) <> 0:
+                if (norm.dot(norm)*part.imass) != 0:
                     factor = 2 * part.vel.dot(norm) / (norm.dot(norm)*part.imass)
                 else: 
                     factor = 0
@@ -651,7 +651,7 @@
             
             for collision in collisions:
                 other = collision.part
-                if other <> part:
+                if other != part:
                     partToOther = part.pos.to(other.pos)
                     distance = partToOther.length()
                     radius = part.radius + other.radius
@@ -674,7 +674,7 @@
 
                                 if not tailtotail: # ignore tail to tail collisions
                                     if part.index < other.index:
-                                        if other.__class__ <> Switch:
+                                        if other.__class__ != Switch:
                                             k.sound.play('part', clamp((relvel.length()-50)/200, 0, 1))
                                         SparkGroup(part.pos+partToOther*0.5, int(factor/10))
                                         if not part.player and other.player:
@@ -705,4 +705,4 @@
             collision.p.vel -= collision.f * collision.n * collision.p.imass
 
 
-             
\ No newline at end of file
+             
--- a/levels/bonus001.py
+++ b/levels/bonus001.py
@@ -13,7 +13,7 @@
     
     for x in range(xn):
         for y in range(yn):
-            if x <> xn/2 and y <> yn/2:
+            if x != xn//2 and y != yn//2:
                 pos = ((x+1.0)*w/(xn+1.0), (y+1.0)*h/(yn+1.0))
                 k.particles.add(Chain({'pos': pos, 'color': 'blue'}))
                 
--- a/src/Input.py
+++ b/src/Input.py
@@ -96,7 +96,7 @@
                         self.onExit()
                         return
                     log('menu exit (escape)', event, log='input')
-                    if k.level.name <> "menu":
+                    if k.level.name != "menu":
                         k.level.menuExit("menu")
                     else:
                         self.onExit()
@@ -105,7 +105,7 @@
                 elif event.key == 275: # right
                     pygame.event.clear()
                     if k.debug and event.mod == pygame.KMOD_LMETA:
-                        k.score, k.level.time = sys.maxint, sys.maxint
+                        k.score, k.level.time = sys.maxsize, sys.maxsize
                         k.config.score(k.level.number)
                     else:
                         k.config.abort()
@@ -125,7 +125,7 @@
                 elif event.key in [49, 50, 51]:
                     if k.debug:
                         k.config.stage = event.key-48
-                        k.score, k.level.time = sys.maxint, sys.maxint
+                        k.score, k.level.time = sys.maxsize, sys.maxsize
                         k.config.score(k.level.number)
                         k.level.restart()
                 # ------------------------------------------------------------------ volume
--- a/src/Sound.py
+++ b/src/Sound.py
@@ -50,7 +50,7 @@
         if volume > 0 and self.soundVolume > 0:
             log(sound, volume, force, event, log='sound')
             volume = clamp(volume, 0.1, 1.0)
-            if self.sounds.has_key(sound):
+            if sound in self.sounds:
                 if force:
                     channel = pygame.mixer.find_channel(1)
                     channel.play(self.sounds[sound])
@@ -110,7 +110,7 @@
     #-------------------------------------------------------------------------------------------
     def setSoundVolume (self, volume):
         log(volume, log='sound')
-        play = self.soundVolume <> clamp(volume, 0.0, 1.0)
+        play = self.soundVolume != clamp(volume, 0.0, 1.0)
         self.soundVolume = clamp(volume, 0.0, 1.0)
         for key in self.sounds.keys():
             self.sounds[key].set_volume(self.soundVolume)
@@ -132,4 +132,4 @@
         for i in range(6):
             if abs(v-i*i/25.0) < 0.01: return i
         return 5
-        
\ No newline at end of file
+        
--- a/src/test.py
+++ b/src/test.py
@@ -74,9 +74,9 @@
     def traverseChain (self, source, chains):
         chains.append(self)
         if self.links:
-            if self.links[0] <> source:                
+            if self.links[0] != source:                
                 self.links[0].traverseChain(self, chains)
-            elif len(self.links) > 1 and self.links[1] <> source:
+            elif len(self.links) > 1 and self.links[1] != source:
                 self.links[1].traverseChain(self, chains)
     
     #-------------------------------------------------------------------------------------------    
@@ -110,7 +110,7 @@
         log("unlinking" , self)
             
         for cp in self.chain:
-            if cp <> self and self in cp.chain:
+            if cp != self and self in cp.chain:
                 cp.chain.remove(self)
                 
         for cp in self.links:
@@ -149,4 +149,4 @@
     chains[3].link(chains[4])
     
     chains[2].unlink()
-    
\ No newline at end of file
+    
--- a/src/Tools.py
+++ b/src/Tools.py
@@ -8,7 +8,7 @@
 
 def quadMirrorSurface (surface, size):
   
-  rect = pygame.Rect(0,0,size[0]/2,size[1]/2)
+  rect = pygame.Rect(0,0,size[0]//2,size[1]//2)
   quad = pygame.transform.scale(surface, rect.size)
   mirror = pygame.Surface(size)
   
@@ -30,8 +30,8 @@
   
 #-----------------------------------------------------------------------------------------------
 def timeString (secs):
-    mins = secs/60
-    hour = mins/60
+    mins = secs//60
+    hour = mins//60
     return "%02d:%02d:%02d" % (hour, mins%60, secs%60)
 
 #-----------------------------------------------------------------------------------------------
@@ -42,12 +42,12 @@
     rect = txt.get_rect().move(pos)
 
     if align == 'center':
-        rect = rect.move(-rect.width/2, 0)
+        rect = rect.move(-rect.width//2, 0)
     elif align == 'right':
         rect = rect.move(-rect.width, 0)
 
     if valign == 'center':
-        rect = rect.move(0, -rect.height/2)
+        rect = rect.move(0, -rect.height//2)
     
     return txt, rect
 
@@ -65,7 +65,7 @@
 
 def image (name):
 
-    if not images.has_key(name):
+    if name not in images:
         file = 'levels/images/' + name + '.png'
         images[name] = pygame.image.load(file)
     return images[name]
@@ -77,10 +77,10 @@
 
     size = (size == 'small') and 's' or 't'
     img = image('cockpit_'+size+'_l')
-    largeRect = pygame.Rect(rect.left-img.get_width(), rect.top-(img.get_height()-rect.height)/2, rect.width+img.get_width()*2, img.get_height())
-    leftRect = pygame.Rect(rect.left-img.get_width(), rect.top-(img.get_height()-rect.height)/2, img.get_width(), img.get_height())
-    midRect = pygame.Rect(rect.left, rect.top-(img.get_height()-rect.height)/2, rect.width, img.get_height())
-    rightRect = pygame.Rect(rect.right, rect.top-(img.get_height()-rect.height)/2, img.get_width(), img.get_height())
+    largeRect = pygame.Rect(rect.left-img.get_width(), rect.top-(img.get_height()-rect.height)//2, rect.width+img.get_width()*2, img.get_height())
+    leftRect = pygame.Rect(rect.left-img.get_width(), rect.top-(img.get_height()-rect.height)//2, img.get_width(), img.get_height())
+    midRect = pygame.Rect(rect.left, rect.top-(img.get_height()-rect.height)//2, rect.width, img.get_height())
+    rightRect = pygame.Rect(rect.right, rect.top-(img.get_height()-rect.height)//2, img.get_width(), img.get_height())
 
     k.screen.blit(img, leftRect)
     img = image('cockpit_'+size+'_m')
@@ -109,7 +109,7 @@
     blend = pygame.surface.Surface((blend_size, blend_size))
     blend.fill((0,0,0))
     blend.set_alpha(255-255*darkness/100)
-    for x in range(surface.get_width()/blend_size+1):
-        for y in range(surface.get_height()/blend_size+1):
+    for x in range(surface.get_width()//blend_size+1):
+        for y in range(surface.get_height()//blend_size+1):
             surface.blit(blend, (x*blend_size, y*blend_size))
 
--- a/src/Effect.py
+++ b/src/Effect.py
@@ -25,7 +25,7 @@
             array = pygame.surfarray.pixels_alpha(self.sparkImages[-1])
             for x in range(8):
                 for y in range(8):
-                    array[x][y] = max(0, array[x][y]-128.0*i/self.sparkShades)
+                    array[x][y] = max(0, array[x][y]-128.0*i//self.sparkShades)
         
     #-------------------------------------------------------------------------------------------
     def reset (self):
--- a/levels/menu.py
+++ b/levels/menu.py
@@ -14,23 +14,23 @@
     
     k.particles.add(Switch({ 'text': 'Play',
                              'action': 'k.config.numSolvedLevels() and k.level.menuExit("menu_play") or k.level.startExit()',
-                             'pos': pos(1*k.world.rect.width/4, 1*k.world.rect.height/3)}))
+                             'pos': pos(1*k.world.rect.width//4, 1*k.world.rect.height//3)}))
 
     k.particles.add(Switch({ 'text': 'Scores', 
                              'align': 'right',
                              'action': 'k.level.menuExit("menu_scores")',
-                             'pos': pos(3*k.world.rect.width/4, 1*k.world.rect.height/3)}))
+                             'pos': pos(3*k.world.rect.width//4, 1*k.world.rect.height//3)}))
 
     k.particles.add(Switch({ 'text': 'Sound',
                              'action': 'k.level.menuExit("menu_sound")',
-                             'pos': pos(1*k.world.rect.width/4, 4*k.world.rect.height/7)}))
+                             'pos': pos(1*k.world.rect.width//4, 4*k.world.rect.height//7)}))
 
     k.particles.add(Switch({ 'text': 'Screen', 
                              'align': 'right',
                              'action': 'k.level.menuExit("menu_screen")',
-                             'pos': pos(3*k.world.rect.width/4, 4*k.world.rect.height/7)}))
+                             'pos': pos(3*k.world.rect.width//4, 4*k.world.rect.height//7)}))
     
     k.particles.add(Switch({ 'text': 'Credits', 
                              'align': 'bottom',
                              'action': 'k.level.menuExit("menu_credits")',
-                             'pos': pos(k.world.rect.width/2, 3*k.world.rect.height/4)}))
+                             'pos': pos(k.world.rect.width//2, 3*k.world.rect.height//4)}))
--- a/src/k.py
+++ b/src/k.py
@@ -36,5 +36,5 @@
 VERSION = "0.7"
 KRANK_TITLE = "Krank version %s"%(VERSION)
 
-print "krank version", VERSION 
+print("krank version", VERSION)
 
--- a/levels/level005.py
+++ b/levels/level005.py
@@ -19,9 +19,9 @@
         for i in range(len(parts)):
             k.particles.add(Particle({'pos': parts[i], 'color': i < 2 and 'blue' or 'white'}))
     else:
-    	if k.config.stage == 2:
-    	    num = 4
-    	else:
+        if k.config.stage == 2:
+            num = 4
+        else:
             num = (k.config.stage-1)*3
         k.particles.ballCircle((cx-xd, cy), 'white',  num, 150)
         k.particles.ballCircle((cx+xd, cy), 'blue',   num, 150)        
