# HG changeset patch # User John "Elwin" Edwards # Date 1389918309 28800 # Node ID 0f4163dbbafc31cdf1855d9bc1e3f26b9bd05f14 # Parent 7c789e87ee5d563c0c932b1c2a3678ef02fe0a3f SVG charts: separate database-querying and chart-printing code. Create Game methods in rlgall.py to get histogram data for score, xl, maxdepth. In stats2.py, make the chart-printing functions use this data and avoid running hard-coded queries. diff -r 7c789e87ee5d -r 0f4163dbbafc py/rlgall.py --- a/py/rlgall.py Thu Jan 16 11:04:45 2014 -0800 +++ b/py/rlgall.py Thu Jan 16 16:25:09 2014 -0800 @@ -296,6 +296,73 @@ for d in data: d["game"] = self return data + def getXLCounts(self, nmax=15): + "Returns a list of (xlevel, gamecount) pairs." + lquery = "SELECT count(*) FROM {0} WHERE xl = %s;".format(self.uname) + mquery = "SELECT count(*) FROM {0} WHERE xl >= %s;".format(self.uname) + try: + nmax = int(nmax) + except (ValueError, TypeError): + return [] + if nmax <= 0: + return [] + xlevels = range(1, nmax) + results = [] + conn = psycopg2.connect("dbname=rlg") + cur = conn.cursor() + for xl in xlevels: + cur.execute(lquery, [xl]) + results.append((xl, cur.fetchone()[0])) + cur.execute(mquery, [nmax]) + results.append((nmax, cur.fetchone()[0])) + cur.close() + conn.close() + return results + def getDepthCounts(self, nmax=30): + "Returns a list of (maxdepth, gamecount) pairs." + dqry = "SELECT count(*) FROM {0} WHERE maxdepth = %s;".format(self.uname) + mqry = "SELECT count(*) FROM {0} WHERE maxdepth >= %s;".format(self.uname) + try: + nmax = int(nmax) + except (ValueError, TypeError): + return [] + if nmax <= 0: + return [] + depths = range(1, nmax) + results = [] + conn = psycopg2.connect("dbname=rlg") + cur = conn.cursor() + for lev in depths: + cur.execute(dqry, [lev]) + results.append((lev, cur.fetchone()[0])) + cur.execute(mqry, [nmax]) + results.append((nmax, cur.fetchone()[0])) + cur.close() + conn.close() + return results + def getScoreCounts(self, blocks=10, size=1000): + "Returns a list of (minscore, gamecount) pairs." + sqry = "SELECT count(*) FROM {0} WHERE score >= %s AND score < %s;".format(self.uname) + mqry = "SELECT count(*) FROM {0} WHERE score >= %s;".format(self.uname) + try: + blocks = int(blocks) + size = int(size) + except (ValueError, TypeError): + return [] + if blocks <= 0 or size <= 0: + return [] + marks = range(blocks - 1) + results = [] + conn = psycopg2.connect("dbname=rlg") + cur = conn.cursor() + for m in marks: + cur.execute(sqry, [m * size, (m + 1) * size]) + results.append((m * size, cur.fetchone()[0])) + cur.execute(mqry, [(blocks - 1) * size]) + results.append(((blocks - 1) * size, cur.fetchone()[0])) + cur.close() + conn.close() + return results # End Game class definition class RogueGame(Game): diff -r 7c789e87ee5d -r 0f4163dbbafc py/stats2.py --- a/py/stats2.py Thu Jan 16 11:04:45 2014 -0800 +++ b/py/stats2.py Thu Jan 16 16:25:09 2014 -0800 @@ -43,6 +43,18 @@ """ +class SVGChart(): + def __init__(self, filename): + self.of = open(filename, "w", encoding="utf-8") + self.of.write(dochead) + def style(self, barcolor): + self.of.write(stylesheet.format(barcolor)) + def write(self, obj): + self.of.write(obj) + def close(self): + self.of.write('\n') + self.of.close() + def ylimits(x): ll = [2, 3, 4, 5, 6, 8, 10, 15] m = 1 @@ -71,14 +83,16 @@ divs = 5 return divs, lim -def mkxlgraph(game, xls, xlcounts): - xlgraph = open("{0}/xl-{1}.svg".format(svgpath, game.uname), "w") - xlgraph.write(dochead) - xlgraph.write(stylesheet.format("0000ff")) +def titles(l, c, r): + return ltitle.format(l) + ctitle.format(c) + rtitle.format(r) + +def mkxlgraph(game, xldata): + xlgraph = SVGChart("{0}/xl-{1}.svg".format(svgpath, game.uname)) + xlgraph.style("0000ff") xlgraph.write(framerect) - xldivs, xlmax = ylimits(max(xlcounts)) + xldivs, xlmax = ylimits(max([ pt[1] for pt in xldata ])) scale = 500 / xlmax - for xl, count in zip(xls, xlcounts): + for xl, count in xldata: barx = xl * 50 + 60 barh = round(scale * count) bary = 550 - barh @@ -90,51 +104,44 @@ xlgraph.write(ylabel.format(labelh, labeln)) xlgraph.write(xlabelf.format("Experience level")) xlgraph.write(ylabelf.format("# of games")) - xlgraph.write(ltitle.format(sitename)) - xlgraph.write(ctitle.format(game.name)) - xlgraph.write(rtitle.format(timestr)) - xlgraph.write('\n') + xlgraph.write(titles(sitename, game.name, timestr)) xlgraph.close() return -def mkscoregraph(game, scoreblocks, scorecounts): +def mkscoregraph(game, scoredata): if isinstance(game, rlgall.ARogueGame): scorewidth = 1500 else: scorewidth = 1000 - scoregraph = open("{0}/score-{1}.svg".format(svgpath, game.uname), "w") - scoregraph.write(dochead) - scoregraph.write(stylesheet.format("ffff00")) + scoregraph = SVGChart("{0}/score-{1}.svg".format(svgpath, game.uname)) + scoregraph.style("ffff00") scoregraph.write(framerect) - scoredivs, scoremax = ylimits(max(scorecounts)) + scoredivs, scoremax = ylimits(max([ pt[1] for pt in scoredata ])) scale = 500 / scoremax - for block, count in zip(scoreblocks, scorecounts): - barx = block * 75 + 100 + for block, count in scoredata: + n = block // scorewidth + barx = n * 75 + 100 barh = round(scale * count) bary = 550 - barh scoregraph.write(barstr.format(75, barh, barx, bary)) - scoregraph.write(xllabel.format(barx, block * scorewidth)) + scoregraph.write(xllabel.format(barx, block)) for yl in range(scoredivs + 1): labeln = int(scoremax * yl / scoredivs) labelh = 550 + 8 - 500 * yl / scoredivs scoregraph.write(ylabel.format(labelh, labeln)) scoregraph.write(xlabelf.format("Score")) scoregraph.write(ylabelf.format("# of games")) - scoregraph.write(ltitle.format(sitename)) - scoregraph.write(ctitle.format(game.name)) - scoregraph.write(rtitle.format(timestr)) - scoregraph.write('\n') + scoregraph.write(titles(sitename, game.name, timestr)) scoregraph.close() return -def mkdeepgraph(game, deeps, deepcounts): - deepgraph = open("{0}/deep-{1}.svg".format(svgpath, game.uname), "w") - deepgraph.write(dochead) - deepgraph.write(stylesheet.format("808000")) +def mkdeepgraph(game, deepdata): + deepgraph = SVGChart("{0}/deep-{1}.svg".format(svgpath, game.uname)) + deepgraph.style("808000") deepgraph.write(framerect) - deepdivs, deepmax = ylimits(max(deepcounts)) + deepdivs, deepmax = ylimits(max([ pt[1] for pt in deepdata ])) scale = 500 / deepmax - for lev, count in zip(deeps, deepcounts): + for lev, count in deepdata: barx = lev * 25 + 75 barh = round(scale * count) bary = 550 - barh @@ -147,48 +154,20 @@ deepgraph.write(ylabel.format(labelh, labeln)) deepgraph.write(xlabelf.format("Deepest dungeon level")) deepgraph.write(ylabelf.format("# of games")) - deepgraph.write(ltitle.format(sitename)) - deepgraph.write(ctitle.format(game.name)) - deepgraph.write(rtitle.format(timestr)) - deepgraph.write('\n') + deepgraph.write(titles(sitename, game.name, timestr)) deepgraph.close() return -con = psycopg2.connect("dbname=rlg") -cur = con.cursor() - for game in rlgall.gamelist: - xlq = "SELECT count(*) FROM {0} WHERE xl = %s;".format(game.uname) - scrq = "SELECT count(*) FROM {0} WHERE score >= %s AND score < %s;".format(game.uname) - deepq = "SELECT count(*) FROM {0} WHERE maxdepth = %s;".format(game.uname) - xls = range(1, 16) - scoreblocks = range(10) - deeps = range(1, 31) - xlcounts = [] - scorecounts = [] - deepcounts = [] + xldata = game.getXLCounts(15) + deepdata = game.getDepthCounts(30) if isinstance(game, rlgall.ARogueGame): scorewidth = 1500 else: scorewidth = 1000 - for xl in xls: - cur.execute(xlq, [xl]) - xlcounts.append(cur.fetchone()[0]) - for sn in scoreblocks: - lscore = sn * scorewidth - if sn == 9: - hscore = scorewidth * 32 - else: - hscore = (sn + 1) * scorewidth - cur.execute(scrq, (lscore, hscore)) - scorecounts.append(cur.fetchone()[0]) - for lev in deeps: - cur.execute(deepq, [lev]) - deepcounts.append(cur.fetchone()[0]) - mkxlgraph(game, xls, xlcounts) - mkscoregraph(game, scoreblocks, scorecounts) - mkdeepgraph(game, deeps, deepcounts) + scoredata = game.getScoreCounts(10, scorewidth) + mkxlgraph(game, xldata) + mkscoregraph(game, scoredata) + mkdeepgraph(game, deepdata) -cur.close() -con.close() exit()