Index: pycodebattler/trunk/pycodebattler/score.py
===================================================================
--- pycodebattler/trunk/pycodebattler/score.py	(revision 47)
+++ pycodebattler/trunk/pycodebattler/score.py	(revision 52)
@@ -4,4 +4,5 @@
 import os
 import sys
+import zlib
 import token
 import parser
@@ -11,29 +12,89 @@
 
 
+def _compseq(seq, level):
+    c = zlib.compressobj(level)
+    return ''.join([c.compress(chunk) for chunk in seq] + [c.flush()])
+
+
+def _maxstrlen(seq, iferr=0):
+    try:
+        return max(len(l) for l in seq)
+    except:
+        return iferr
+
+
 class Scorer:
     def __init__(self, lines):
+        self._code = ''.join(lines)
+        self._ccode = _compseq(lines, 9)
         self._lines = lines
-        self._lnum = len(lines)
-        self._code = ''.join(lines)
-        self._bytes = len(self._code)
+        self._nlines = [l for l in lines
+                        if l.strip() and not l.strip().startswith('#')]
 
-        self._nlines = [l for l in lines if not l.strip().startswith('#')]
-        self._nlnum = len(self._nlines)
-        self._nbytes = sum(len(l) for l in self._nlines)
+        self._astpl = parser.suite(self.code()).totuple()
+        self._token_cache = {}
 
-        self._st = parser.suite(self._code)
-        self._p8c = self._pep8count()
-        self._cpsum, self._cplen = self._complexity()
+        self.complexity()
+        self.style_errors()
 
-    def _complexity(self, item='all'):
+    def tupletree(self):
+        return self._astpl
+
+    def code(self):
+        return self._code
+
+    def lines(self):
+        return self._lines
+
+    def uncommented_lines(self):
+        return self._nlines
+
+    def bytes(self):
+        return len(self.code()) or 1
+
+    def uncommented_bytes(self):
+        return sum(len(l) for l in self.uncommented_lines()) or 1
+
+    def rows(self):
+        return len(self.lines()) or 1
+
+    def uncommented_rows(self):
+        return len(self.uncommented_lines()) or 1
+
+    def compressibility(self, level=6):
+        return float(len(self._ccode)) / self.bytes()
+
+    def token_bytes(self, types):
+        def _bytes(stp, bytes=0):
+            if not stp:
+                return bytes
+            if stp[0] in types:
+                bytes += len(stp[1])
+            else:
+                for p in stp[1:]:
+                    bytes = _bytes(p, bytes)
+            return bytes
+
+        types = tuple(types)
+
+        try:
+            ret = self._token_cache[types]
+        except KeyError:
+            ret = self._token_cache[types] = _bytes(self.tupletree())
+
+        return ret
+
+    def complexity(self, item='all', iferr=(256, 1)):
         try:
             stat = cc.measure_complexity(self._code, item)
             ccpp = cc.PrettyPrinter(None, True)
             cplx = zip(*ccpp.flatten_stats(stat))[-1]
-            return sum(cplx) + 1, len(cplx) + 1
+            self._ccsum = sum(cplx) or 1
+            self._cclen = len(cplx) or 1
         except:
-            return 256, 1
+            self._ccsum, self._cclen = iferr
+        return self._ccsum, self._cclen
 
-    def _pep8count(self, name=''):
+    def style_errors(self, name='', iferr=256):
         pep8.process_options([name])
         p8ck = pep8.Checker(None)
@@ -44,59 +105,59 @@
             tmpfp = sys.stdout
             sys.stdout = open(os.devnull, 'w')
-
-            try:  # nest for 2.4 or earlier
-                return p8ck.check_all() + 1
-            except:
-                return 256
+            self._estyle = p8ck.check_all() or 1
+        except:
+            self._estyle = iferr
         finally:
             sys.stdout = tmpfp
+        return self._estyle
 
-    def _parsebytes(self, types):
-        def litbytes(stp, bytes=0):
-            if not stp:
-                return bytes
-            if stp[0] in types:
-                bytes += len(stp[1])
-            else:
-                for p in stp[1:]:
-                    bytes = litbytes(p, bytes)
-            return bytes
-        return litbytes(self._st.totuple())
+    def complex_score(self, coefficient=32):
+        maxlen = _maxstrlen(self.lines(), 256)
+        score = self._cclen * coefficient
+        score /= (maxlen / (coefficient * 2)) or 1
+        score /= (self._ccsum / ((coefficient / 3) or 1)) or 1
+        score *= self.compressibility()
+        return int(score) or 1
 
-    @staticmethod
-    def _strlen_max(seq, iferr=0):
-        try:
-            return max(len(l) for l in seq)
-        except:
-            return iferr
+    def style_score(self, coefficient=64):
+        data_bytes = self.token_bytes([token.NUMBER, token.STRING,
+                                       token.NAME, token.COLON])
+        score = self.uncommented_bytes() - data_bytes
+        score /= (self.uncommented_rows() / coefficient) or 1
+        score /= self._estyle
+        score *= (coefficient / 12) or 1
+        score *= self.compressibility()
+        return int(score) or 1
 
-    def pep8_score(self):
-        bytes = self._nbytes - self._parsebytes(
-            [token.NUMBER, token.STRING, token.NAME, token.COLON])
-        return (bytes / ((self._nlnum / 256) + 1) / self._p8c) + 1
+    def lines_score(self, coefficient=4):
+        maxlen = _maxstrlen(self.lines(), 256)
+        data_bytes = self.token_bytes([token.NUMBER, token.STRING,
+                                       token.NAME, token.COLON])
+        score = self.uncommented_bytes() - data_bytes
+        score /= (maxlen / self.uncommented_rows()) or 1
+        score /= self.uncommented_rows()
+        score *= coefficient
+        score *= self.compressibility()
+        return int(score) or 1
 
-    def complex_score(self):
-        bmax = self._strlen_max(self._lines, 256)
-        return (32 * self._cplen / ((bmax / 79) + 1) / \
-                ((self._cpsum / 12) + 1)) + 1
+    def const_score(self, coefficient=2):
+        const_bytes = self.token_bytes([token.NUMBER, token.STRING]) or 1
+        score = self.uncommented_bytes() / const_bytes
+        score *= coefficient
+        score *= self.compressibility()
+        return int(score) or 1
 
-    def lines_score(self):
-        bmax = self._strlen_max(self._lines, 256)
-        bytes = self._nbytes - self._parsebytes(
-            [token.NUMBER, token.STRING, token.NAME, token.COLON])
-        return (bytes / ((bmax / (self._nlnum + 1)) + 1) / \
-                (self._nlnum + 1)) + 1
+    def colon_score(self, coefficient=1):
+        colon_bytes = self.token_bytes([token.COLON]) or 1
+        score = self.uncommented_bytes()
+        score /= (score / colon_bytes) or 1
+        score *= coefficient
+        score *= self.compressibility()
+        return int(score) or 1
 
-    def const_score(self):
-        cbytes = self._parsebytes([token.NUMBER, token.STRING]) + 1
-        return (self._nbytes + 1) // cbytes
-
-    def colon_score(self):
-        bytes = self._nbytes + 1
-        cbytes = self._parsebytes([token.COLON]) + 1
-        return (bytes // ((bytes // cbytes) + 1)) + 1
-
-    def name_score(self):
-        bytes = self._nbytes + 1
-        nbytes = self._parsebytes([token.NAME]) + 1
-        return bytes // ((nbytes / 16) + 1)
+    def name_score(self, coefficient=16):
+        name_bytes = self.token_bytes([token.NAME]) or 1
+        score = self.uncommented_bytes()
+        score /= (name_bytes / coefficient) or 1
+        score *= self.compressibility()
+        return int(score) or 1
