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

--- a/astar.py
+++ b/astar.py
@@ -2,7 +2,7 @@
 
 # Revamped to use much saner data structures
 
-import sets, time
+import time
 import collide, objects, euclid
 
 class Path:
@@ -82,7 +82,7 @@
 
     def _findPath(self, fromlocation, tolocation):
         self.on = {}
-        self.c = sets.Set()
+        self.c = set()
 
         end = tolocation
         fnode = self.mh.getNode(fromlocation)
@@ -117,7 +117,7 @@
         if not isinstance(box, collide.AABox):
             return
         x = int(box.xmin)
-        self.max_y = 0
+        self.max_y = '\x00'
         r = self.resolution
 
         for x in range(int(box.xmin - box.xmin%r),
@@ -194,13 +194,13 @@
     ]
     def findClosest(self, sx, sy):
         x, y = self.toCoords(sx, sy)
-        sx -= self.resolution / 2
-        sy -= self.resolution / 2
-        to_search = sets.Set([(x,y)])
-        searched = sets.Set()
+        sx -= self.resolution // 2
+        sy -= self.resolution // 2
+        to_search = set([(x,y)])
+        searched = set()
         while 1:
             if len(searched) == len(self.m):
-                raise ValueError, "didn't find a closest point?!?"
+                raise ValueError("didn't find a closest point?!?")
             distances = []
             for t in list(to_search):
                 if t in searched: continue
@@ -226,7 +226,7 @@
         for x,y in self.m.keys():
             xmin = min(xmin, x); xmax = max(xmax, x)
             ymin = min(ymin, y); ymax = max(ymax, y)
-        print 'X %s -> %s;   Y %s -> %s'%(xmin, xmax, ymin, ymax)
+        print('X %s -> %s;   Y %s -> %s'%(xmin, xmax, ymin, ymax))
         l = []
         for y in range(ymin, ymax + 4, 4):
             r = ['%3d '%y]
--- a/euclid.py
+++ b/euclid.py
@@ -33,6 +33,8 @@
 import operator
 import types
 
+from past.builtins import long
+
 # Some magic here.  If _use_slots is True, the classes will derive from
 # object and will define a __slots__ class variable.  If _use_slots is
 # False, classes will be old-style and will not define __slots__.
@@ -112,7 +114,7 @@
             return tuple([(self.x, self.y)['xy'.index(c)] \
                           for c in name])
         except ValueError:
-            raise AttributeError, name
+            raise AttributeError(name)
 
     if _enable_swizzle_set:
         # This has detrimental performance on ordinary setattr as well
@@ -127,7 +129,7 @@
                         l['xy'.index(c)] = v
                     self.x, self.y = l
                 except ValueError:
-                    raise AttributeError, name
+                    raise AttributeError(name)
 
     def __add__(self, other):
         if isinstance(other, Vector2):
@@ -312,7 +314,7 @@
             return tuple([(self.x, self.y, self.z)['xyz'.index(c)] \
                           for c in name])
         except ValueError:
-            raise AttributeError, name
+            raise AttributeError(name)
 
     if _enable_swizzle_set:
         # This has detrimental performance on ordinary setattr as well
@@ -327,7 +329,7 @@
                         l['xyz'.index(c)] = v
                     self.x, self.y, self.z = l
                 except ValueError:
-                    raise AttributeError, name
+                    raise AttributeError(name)
 
 
     def __add__(self, other):
@@ -1160,12 +1162,12 @@
 
 class Geometry:
     def _connect_unimplemented(self, other):
-        raise AttributeError, 'Cannot connect %s to %s' % \
-            (self.__class__, other.__class__)
+        raise AttributeError('Cannot connect %s to %s' % \
+            (self.__class__, other.__class__))
 
     def _intersect_unimplemented(self, other):
-        raise AttributeError, 'Cannot intersect %s and %s' % \
-            (self.__class__, other.__class__)
+        raise AttributeError('Cannot intersect %s and %s' % \
+            (self.__class__, other.__class__))
 
     _intersect_point2 = _intersect_unimplemented
     _intersect_line2 = _intersect_unimplemented
@@ -1341,18 +1343,18 @@
                 self.p = args[0].copy()
                 self.v = args[1].copy()
             else:
-                raise AttributeError, '%r' % (args,)
+                raise AttributeError('%r' % (args,))
         elif len(args) == 1:
             if isinstance(args[0], Line2):
                 self.p = args[0].p.copy()
                 self.v = args[0].v.copy()
             else:
-                raise AttributeError, '%r' % (args,)
+                raise AttributeError('%r' % (args,))
         else:
-            raise AttributeError, '%r' % (args,)
+            raise AttributeError('%r' % (args,))
         
         if not self.v:
-            raise AttributeError, 'Line has zero-length vector'
+            raise AttributeError('Line has zero-length vector')
 
     def __copy__(self):
         return self.__class__(self.p, self.v)
@@ -1685,18 +1687,18 @@
                 self.p = args[0].copy()
                 self.v = args[1].copy()
             else:
-                raise AttributeError, '%r' % (args,)
+                raise AttributeError('%r' % (args,))
         elif len(args) == 1:
             if isinstance(args[0], Line3):
                 self.p = args[0].p.copy()
                 self.v = args[0].v.copy()
             else:
-                raise AttributeError, '%r' % (args,)
+                raise AttributeError('%r' % (args,))
         else:
-            raise AttributeError, '%r' % (args,)
+            raise AttributeError('%r' % (args,))
         
         if not self.v:
-            raise AttributeError, 'Line has zero-length vector'
+            raise AttributeError('Line has zero-length vector')
 
     def __copy__(self):
         return self.__class__(self.p, self.v)
@@ -1840,13 +1842,13 @@
                 self.n = args[0].copy()
                 self.k = args[1]
             else:
-                raise AttributeError, '%r' % (args,)
+                raise AttributeError('%r' % (args,))
 
         else:
-            raise AttributeError, '%r' % (args,)
+            raise AttributeError('%r' % (args,))
         
         if not self.n:
-            raise AttributeError, 'Points on plane are colinear'
+            raise AttributeError('Points on plane are colinear')
 
     def __copy__(self):
         return self.__class__(self.n, self.k)
--- a/objloader.py
+++ b/objloader.py
@@ -12,7 +12,7 @@
         if values[0] == 'newmtl':
             mtl = contents[values[1]] = {}
         elif mtl is None:
-            raise ValueError, "mtl file doesn't start with newmtl stmt"
+            raise ValueError("mtl file doesn't start with newmtl stmt")
         elif values[0] == 'map_Kd':
             # load the texture referred to by this declaration
             mtl[values[0]] = values[1]
@@ -28,7 +28,7 @@
             glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, ix, iy, 0, GL_RGBA,
                 GL_UNSIGNED_BYTE, image)
         else:
-            mtl[values[0]] = map(float, values[1:])
+            mtl[values[0]] = list(map(float, values[1:]))
     return contents
 
 class OBJ:
@@ -49,17 +49,17 @@
             values = line.split()
             if not values: continue
             if values[0] == 'v':
-                v = map(float, values[1:4])
+                v = list(map(float, values[1:4]))
                 if swapyz:
                     v = v[0], v[2], v[1]
                 self.vertices.append(v)
             elif values[0] == 'vn':
-                v = map(float, values[1:4])
+                v = list(map(float, values[1:4]))
                 if swapyz:
                     v = v[0], v[2], v[1]
                 self.normals.append(v)
             elif values[0] == 'vt':
-                self.texcoords.append(map(float, values[1:3]))
+                self.texcoords.append(list(map(float, values[1:3])))
             elif values[0] in ('usemtl', 'usemat'):
                 material = values[1]
             elif values[0] == 'mtllib':
--- a/pyglyph/font.py
+++ b/pyglyph/font.py
@@ -154,8 +154,8 @@
             except:
                 pass
         if not filename:
-            raise Exception, 'Font \"%s\" (bold=%s,italic=%s) not found' % \
-                (family, bold, italic)
+            raise Exception('Font \"%s\" (bold=%s,italic=%s) not found' % \
+                (family, bold, italic))
         fake_bold = bold and not b
         fake_italic = italic and not i
 
@@ -264,7 +264,7 @@
         w, h = self.pygame_font.size(self.charset)
         w += len(self.charset) * MIN_TEXTURE_CHARACTER_SPACE
         if w > MAX_TEXTURE_WIDTH:
-            h = (w / MAX_TEXTURE_WIDTH + 1) * h
+            h = (w // MAX_TEXTURE_WIDTH + 1) * h
             w = MAX_TEXTURE_WIDTH
 
         # Round up to valid texture size
@@ -273,7 +273,7 @@
         # Initialise empty surface to draw text onto.  Problems with
         # luminance-alpha, so using RGBA.
         data = chr(0) * w * h * 4
-        surface = pygame.image.fromstring(data, (w, h), 'RGBA')
+        surface = pygame.image.fromstring(data.encode(), (w, h), 'RGBA')
 
         # Render each glyph to the surface, recording each bounding box
         # in tex_boxmap.
--- a/pyglyph/html.py
+++ b/pyglyph/html.py
@@ -77,7 +77,7 @@
 __docformat__ = 'restructuredtext'
 __version__ = '$Id: html.py,v 1.5 2006/05/25 14:14:40 alex Exp $'
 
-import HTMLParser
+from html.parser import HTMLParser
 import re
 
 import pyglyph
@@ -176,11 +176,11 @@
         return pyglyph.layout.ParagraphMarker(style, 
             justification=justification)
 
-class Parser(HTMLParser.HTMLParser):
+class Parser(HTMLParser):
     whitespace_pattern = re.compile('[ \t\r\n]+')
 
     def __init__(self, font_factory):
-        HTMLParser.HTMLParser.__init__(self)
+        HTMLParser.__init__(self)
         self.runs = []
         self.paragraph_stack = [(None, ParagraphPrototype())]
         self.style_stack = [(None, StylePrototype(font_factory))]
--- a/pyglyph/ttf.py
+++ b/pyglyph/ttf.py
@@ -99,7 +99,7 @@
         self._tables = {}
         for table in _read_table_directory_entry.array(self._data, 
             offsets.size, offsets.num_tables):
-            self._tables[table.tag] = table
+            self._tables[table.tag.decode()] = table
 
         self._names = None
         self._horizontal_metrics = None
@@ -378,7 +378,7 @@
         # format ever.  Whoever the fuckwit is that thought this up is
         # a fuckwit. 
         header = _read_cmap_format4Header(self._data, offset)
-        seg_count = header.seg_count_x2 / 2
+        seg_count = header.seg_count_x2 // 2
         array_size = struct.calcsize('>%dH' % seg_count)
         end_count = self._read_array('>%dH' % seg_count, 
             offset + header.size)
--- a/farmer.py
+++ b/farmer.py
@@ -34,7 +34,7 @@
     def setDifficult(self, difficulty):
         self.speed = .05 * difficulty
 
-    class RabbitCaught:
+    class RabbitCaught(BaseException):
         pass
 
 
@@ -103,16 +103,16 @@
                     return   # NO MOVE
                 new_dest = self.path.nodes.pop().l
             else:
-                if self.path:
+                if self.path and len(self.path.nodes) > 0:
                     end = self.path.nodes[0].l
                 else:
-                    end = self.destination
+                    end = tuple(self.destination)
                 if rabbit_pos != end:
                     # little bugger moved!
                     #print 'RABBIT MOVED'
                     self.path = level.grid.findPath(self_pos, rabbit_pos)
                     if self.path is None:
-                        print 'RABBIT MOVED and we have no path'
+                        print('RABBIT MOVED and we have no path')
                         self.state = self.ST_OUTSIDE
                         return   # NO MOVE
                     new_dest = self.path.nodes.pop().l
@@ -124,14 +124,14 @@
                     #print 'EMPTY PATH'
                     self.path = level.grid.findPath(self_pos, rabbit_pos)
                     if self.path is None:
-                        print 'destination and no path'
+                        print('destination and no path')
                         self.state = self.ST_OUTSIDE
                         return   # NO MOVE
                 new_dest = self.path.nodes.pop().l
 
             # set the dest and the end-check sphere
             if new_dest is not None:
-                s = level.grid.resolution / 2
+                s = level.grid.resolution // 2
                 self.destination = euclid.Point3(new_dest[0]+s, 0,
                     new_dest[1]+s)
                 self.dest_sp = euclid.Sphere(euclid.Point3(new_dest[0]+s,
--- a/shaders.py
+++ b/shaders.py
@@ -46,7 +46,7 @@
     if length.value > 0:
         log = create_string_buffer(length.value)
         glGetInfoLogARB(shader, length, byref(length), log)
-        print >> sys.stderr, log.value
+        print(log.value, file=sys.stderr)
 
 def compile_shader(source, shader_type):
     shader = glCreateShaderObjectARB(shader_type)
--- a/collide.py
+++ b/collide.py
@@ -51,15 +51,15 @@
             c = euclid.Point3(*c)
         c = self.__c = c
 
-        x = (c.x - self.dx/2, c.x + self.dx/2)
+        x = (c.x - self.dx//2, c.x + self.dx//2)
         self.xmin = min(x)
         self.xmax = max(x)
 
-        y = (c.y - self.dy/2, c.y + self.dy/2)
+        y = (c.y - self.dy//2, c.y + self.dy//2)
         self.ymin = min(y)
         self.ymax = max(y)
 
-        z = (c.z - self.dz/2, c.z + self.dz/2)
+        z = (c.z - self.dz//2, c.z + self.dz//2)
         self.zmin = min(z)
         self.zmax = max(z)
 
@@ -79,7 +79,7 @@
                     self.zmin < other.z < self.zmax)
         elif isinstance(other, AABox):
             c = other.c
-            hdx, hdy, hdz = other.dx/2, other.dy/2, other.dz/2
+            hdx, hdy, hdz = other.dx//2, other.dy//2, other.dz//2
             return (self.xmin-hdx < c.x < self.xmax+hdx and
                 self.ymin-hdy < c.y < self.ymax+hdy and
                 self.zmin-hdz < c.z < self.zmax+hdz)
@@ -141,15 +141,15 @@
 
         # now see which axis has the dot product closest to 1
         if y > x and y > z:
-            c.y = self.ymax + other.dy/2
+            c.y = self.ymax + other.dy//2
             vmod = euclid.Vector3(1, 0, 1)
         elif x > z:
-            hdx = other.dx/2
+            hdx = other.dx//2
             if c.x > self.c.x: c.x = self.xmax + hdx
             else: c.x = self.xmin - hdx
             vmod = euclid.Vector3(0, 1, 1)
         else:
-            hdz = other.dz/2
+            hdz = other.dz//2
             if c.z > self.c.z: c.z = self.zmax + hdz
             else: c.z = self.zmin - hdz
             vmod = euclid.Vector3(1, 1, 0)
--- a/game.py
+++ b/game.py
@@ -22,8 +22,8 @@
 class Game(ui.UserInterface):
     def __init__(self, viewport):
         self.viewport = viewport
-        hx = self.viewport[0]/2
-        hy = self.viewport[1]/2
+        hx = self.viewport[0]//2
+        hy = self.viewport[1]//2
         pygame.mouse.set_visible(False)
 
         glLightfv(GL_LIGHT0, GL_POSITION,  (100, 200, 100, 0.0))
@@ -85,13 +85,13 @@
             glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
             glPushMatrix()
             tex = textures.textures['bouncy-title']
-            glTranslate(x/2 - tex.width/2, y-tex.height, 0)
+            glTranslate(x//2 - tex.width//2, y-tex.height, 0)
             tex.render()
             glPopMatrix()
 
             #glDisable(GL_BLEND)
             pyglyph.begin()
-            instructions.draw(pos=(x/2, y - tex.height),
+            instructions.draw(pos=(x//2, y - tex.height),
                 anchor=(pyglyph.Align.center, pyglyph.Align.top))
             pyglyph.end()
 
--- a/leveledit.py
+++ b/leveledit.py
@@ -127,7 +127,7 @@
         reader = csv.reader(open(self.filename), excel_colon)
         for name, position, rotation in list(reader)[1:]:
             if name[0] == '#': continue
-            position = map(float, position.split(','))
+            position = list(map(float, position.split(',')))
             rotation = float(rotation)
             object = objects.map_elements[name](rotation, position)
             self.objects.append(object)
@@ -198,7 +198,7 @@
         self.do_reset()
 
         move = False
-        vx, vy = (self.width/2, self.height/2)
+        vx, vy = (self.width//2, self.height//2)
         while self.running:
             ts = clock.tick(30)
             turn = 0
@@ -263,9 +263,9 @@
 
             if self.active_add is not None and x > 120:
                 x, y = pygame.mouse.get_pos()
-                w2, h2 = self.width/2, self.height/2
+                w2, h2 = self.width//2, self.height//2
                 offx, offy = w2 - vx, h2 - vy
-                tx, ty = (x - w2 + offx)/scale, (y - h2 - offy)/scale
+                tx, ty = (x - w2 + offx)//scale, (y - h2 - offy)//scale
                 glTranslate(tx, 0, ty)
                 glRotate(self.obj_rotation, 0, 1, 0)
                 glCallList(self.active_add.obj.gl_list)
@@ -380,7 +380,7 @@
         glEnd()
 
         pyglyph.begin()
-        text.draw(pos=(x/2, y/2),
+        text.draw(pos=(x//2, y//2),
             anchor=(pyglyph.Align.center, pyglyph.Align.center))
         pyglyph.end()
 
--- a/map.py
+++ b/map.py
@@ -20,7 +20,7 @@
         self.pieces = []
         for name, position, rotation in list(reader)[1:]:
             if name[0] == '#': continue
-            position = map(float, position.split(','))
+            position = list(map(float, position.split(',')))
             rotation = float(rotation)
 
             if name == 'player':
@@ -45,7 +45,7 @@
         l = []
         if isinstance(hitbox, collide.Sphere) and hitbox.c.y < hitbox.r:
             l.append(self)
-        elif isinstance(hitbox, collide.AABox) and hitbox.c.y < hitbox.dy/2:
+        elif isinstance(hitbox, collide.AABox) and hitbox.c.y < hitbox.dy//2:
             l.append(self)
         for piece in self.pieces:
             if (piece.hitbox is not None and
@@ -59,7 +59,7 @@
             hitbox.c.y = hitbox.r
             obj.velocity.y = 0
         elif isinstance(hitbox, collide.AABox):
-            hitbox.c.y = hitbox.dy/2
+            hitbox.c.y = hitbox.dy//2
         return hitbox.c, euclid.Vector3(1, 0, 1)
 
     def find_food(self, other):
@@ -73,7 +73,7 @@
                 l.append((distance, piece))
         if not l:
             return None
-        l.sort()
+        l.sort(key=lambda x: x[0])
         return l[0][1]
 
     def eat_food(self, food):
--- a/pyglyph/layout.py
+++ b/pyglyph/layout.py
@@ -8,6 +8,8 @@
 import pyglyph
 import pyglyph.font
 
+from functools import reduce
+
 __docformat__ = 'restructuredtext'
 __version__ = '$Id: layout.py,v 1.4 2006/05/25 14:14:40 alex Exp $'
 
@@ -200,7 +202,7 @@
             if self.justification == Align.right:
                 x = self.width - self.current_line_width
             elif self.justification == Align.center:
-                x = (self.width - self.current_line_width) / 2
+                x = (self.width - self.current_line_width) // 2
             for run in self.current_line:
                 run.translate(x, self.y)
             self.lines.append(StyledRunLine(self.current_line,
@@ -332,11 +334,11 @@
 
         x, y = pos
         if anchor[0] == pyglyph.Align.center:
-            x -= self.width/2
+            x -= self.width//2
         elif anchor[0] == pyglyph.Align.right:
             x -= self.width
         if anchor[1] == pyglyph.Align.center:
-            y += self.height/2
+            y += self.height//2
         elif anchor[1] == pyglyph.Align.bottom:
             y += self.height
 
--- a/showobj.py
+++ b/showobj.py
@@ -14,8 +14,8 @@
 
 pygame.init()
 viewport = (256,256)
-hx = viewport[0]/2
-hy = viewport[1]/2
+hx = viewport[0]//2
+hy = viewport[1]//2
 srf = pygame.display.set_mode(viewport, OPENGL | DOUBLEBUF)
 
 glLightfv(GL_LIGHT0, GL_POSITION,  (-40, 200, 100, 0.0))
--- a/ui.py
+++ b/ui.py
@@ -39,7 +39,7 @@
         glEnd()
 
         pyglyph.begin()
-        text.draw(pos=(x/2, y/2),
+        text.draw(pos=(x//2, y//2),
             anchor=(pyglyph.Align.center, pyglyph.Align.center))
         pyglyph.end()
 
--- a/rabbit.py
+++ b/rabbit.py
@@ -88,7 +88,7 @@
         if self.state is self.ST_EATING:
             self.state = self.ST_SITTING
 
-    class RabbitFed:
+    class RabbitFed(BaseException):
         pass
 
     GRAVITY = 1.5/1000.
