view py/stats2.py @ 72:1d8dc3ed22cf

Restore the default size of the high score list. In an era of megabyte JavaScript libraries, ten lines of text is not too much.
author John "Elwin" Edwards
date Sun, 20 Mar 2016 07:59:22 -0400
parents 876786c55450
children
line wrap: on
line source

#!/usr/bin/python3

import psycopg2
import rlgall
from datetime import datetime

sitename = "rlgallery.org"
svgpath = rlgall.webdir
timestr = datetime.utcnow().strftime("%Y-%m-%d %H:%M")

dochead = """<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="900" height="600">
"""
stylesheet = """<style type="text/css">
rect.frame {{
 fill:#ffffff;
 fill-opacity:1;
 stroke:#000000;
 stroke-width:2;
 stroke-opacity:1;
}}
rect.bar {{
 fill:#{0};
 fill-opacity:1;
 stroke:#000000;
 stroke-width:2;
 stroke-opacity:1;
}}
g.bar3d polygon {{
 fill:#{0};
 fill-opacity:1;
 stroke:#000000;
 stroke-width:1;
 stroke-opacity:1;
}}
</style>
"""
framerect = '<rect width="750" height="500" x="100" y="50" class="frame"/>\n'
barstr = '<rect width="{0}" height="{1}" x="{2}" y="{3}" class="bar"/>\n'
xllabel = '<text x="{0}" y="570" font-size="15" text-anchor="middle">{1}</text>\n'
ylabel = '<text x="90" y="{0:.2f}" font-size="16" text-anchor="end">{1}</text>\n'
xlabelf = '<text x="475" y="590" font-size="15" text-anchor="middle">{0}</text>\n'
ltitle = '<text x="100" y="35" font-size="16" text-anchor="start">{0}</text>\n'
ctitle = '<text x="475" y="34" font-size="18" text-anchor="middle">{0}</text>\n'
rtitle = '<text x="850" y="35" font-size="16" text-anchor="end">{0}</text>\n'
ylabelf = """<g transform="translate(100, 300)">
 <g transform="rotate(-90)">
 <text x="0" y="-60" font-size="16" text-anchor="middle">{0}</text>
 </g>
</g>
"""

def makepolygon(coords):
  points = [ "{0:.2f},{1:.2f}".format(pt[0], pt[1]) for pt in coords ]
  pointstr = " ".join(points)
  return '<polygon points="{}"/>\n'.format(pointstr)

def bar3d(x, w, h):
  ydelta = w / 2
  xdelta = ydelta / 3
  flowerleft = (x, 550)
  flowerright = (x + w, 550)
  fupperleft = (x, 550 - h)
  fupperright = (x + w, 550 - h)
  blowerright = (x + w + xdelta, 550 - ydelta)
  bupperleft = (x + xdelta, 550 - h - ydelta)
  bupperright = (x + w + xdelta, 550 - h - ydelta)
  frontface = makepolygon([flowerleft, flowerright, fupperright, fupperleft])
  rightface = makepolygon([blowerright, flowerright, fupperright, bupperright])
  topface = makepolygon([bupperleft, bupperright, fupperright, fupperleft])
  gopen = '<g class="bar3d" clip-path="url(#framer)">\n'
  gclose = '</g>\n'
  return gopen + "".join([frontface, rightface, topface]) + gclose

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('</svg>\n')
    self.of.close()

def ylimits(x):
  ll = [2, 3, 4, 5, 6, 8, 10, 15]
  m = 1
  size = 0
  while True:
    for i in ll:
      if i * m > x * 1.1:
        size = i
        lim = i * m
        break
    if size:
      break
    else:
      m *= 10
  if size in [3, 6]:
    if lim == 3:
      divs = 3
    else:
      divs = 6
  elif size in [4, 8]:
    divs = 4
  else:
    if lim == 2:
      divs = 2
    else:
      divs = 5
  return divs, lim

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)
  xlgraph.write('<clipPath id="framer">\n')
  xlgraph.write(framerect)
  xlgraph.write('</clipPath>\n')
  xldivs, xlmax = ylimits(max([ pt[1] for pt in xldata ]))
  scale = 500 / xlmax
  for xl, count in xldata:
    barx = xl * 50 + 60
    barh = scale * count
    bary = 550 - barh
    if count > 0:
      xlgraph.write(bar3d(barx, 30, barh))
    xlgraph.write(xllabel.format(barx + 15, xl))
  for yl in range(xldivs + 1):
    labeln = int(xlmax * yl / xldivs)
    labelh = 550 + 8 - 500 * yl / xldivs
    xlgraph.write(ylabel.format(labelh, labeln))
  xlgraph.write(xlabelf.format("Experience level"))
  xlgraph.write(ylabelf.format("# of games"))
  xlgraph.write(titles(sitename, game.name, timestr))
  xlgraph.close()
  return

def mkscoregraph(game, scoredata):
  if isinstance(game, rlgall.ARogueGame):
    scorewidth = 1000
  else:
    scorewidth = 500
  scoregraph = SVGChart("{0}/score-{1}.svg".format(svgpath, game.uname))
  scoregraph.style("ffff00")
  scoregraph.write(framerect)
  scoregraph.write('<clipPath id="framer">\n')
  scoregraph.write(framerect)
  scoregraph.write('</clipPath>\n')
  scoredivs, scoremax = ylimits(max([ pt[1] for pt in scoredata ]))
  scale = 500 / scoremax
  for block, count in scoredata:
    n = block // scorewidth
    barx = n * 50 + 100
    barh = scale * count
    bary = 550 - barh
    if count > 0:
      scoregraph.write(bar3d(barx, 50, barh))
    if n % 2 == 0:
      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(titles(sitename, game.name, timestr))
  scoregraph.close()
  return

def mkdeepgraph(game, deepdata):
  deepgraph = SVGChart("{0}/deep-{1}.svg".format(svgpath, game.uname))
  deepgraph.style("808000")
  deepgraph.write(framerect)
  deepgraph.write('<clipPath id="framer">\n')
  deepgraph.write(framerect)
  deepgraph.write('</clipPath>\n')
  deepdivs, deepmax = ylimits(max([ pt[1] for pt in deepdata ]))
  scale = 500 / deepmax
  for lev, count in deepdata:
    barx = lev * 25 + 75
    barh = scale * count
    bary = 550 - barh
    if count > 0:
      deepgraph.write(bar3d(barx, 25, barh))
    if lev % 3 == 0:
      deepgraph.write(xllabel.format(barx + 12.5, lev))
  for yl in range(deepdivs + 1):
    labeln = int(deepmax * yl / deepdivs)
    labelh = 550 + 8 - 500 * yl / deepdivs
    deepgraph.write(ylabel.format(labelh, labeln))
  deepgraph.write(xlabelf.format("Deepest dungeon level"))
  deepgraph.write(ylabelf.format("# of games"))
  deepgraph.write(titles(sitename, game.name, timestr))
  deepgraph.close()
  return

for game in rlgall.gamelist:
  xldata = game.getXLCounts(15)
  deepdata = game.getDepthCounts(30)
  if isinstance(game, rlgall.ARogueGame):
    scorewidth = 1000
  else:
    scorewidth = 500
  scoredata = game.getScoreCounts(15, scorewidth)
  mkxlgraph(game, xldata)
  mkscoregraph(game, scoredata)
  mkdeepgraph(game, deepdata)

exit()