X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~yarrgweb/git?p=ypp-sc-tools.main.git;a=blobdiff_plain;f=yoweb-scrape;h=d2d194c213e9039b941a1465c464239654ca14ae;hp=e7e3ec79bd44d952dee663d0874324d805449014;hb=9cec9bede40ddd31329097b6952d7a72ec005bbc;hpb=9e7deaa661702a20b4aaca1967b572290edfec1a diff --git a/yoweb-scrape b/yoweb-scrape index e7e3ec7..d2d194c 100755 --- a/yoweb-scrape +++ b/yoweb-scrape @@ -14,6 +14,7 @@ import sys import re as regexp import random import curses +import termios from optparse import OptionParser from BeautifulSoup import BeautifulSoup @@ -27,8 +28,20 @@ puzzles = ('Swordfighting/Bilging/Sailing/Rigging/Navigating'+ '/Drinking/Spades/Hearts/Treasure Drop/Poker/Distilling'+ '/Alchemistry/Shipwrightery/Blacksmithing/Foraging').split('/') +core_duty_puzzles = [ + 'Gunning', + ['Sailing','Rigging'], + 'Bilging', + 'Carpentry', + ] + +duty_puzzles = ([ 'Navigating', 'Battle Navigation' ] + + core_duty_puzzles + + [ 'Treasure Haul' ]) + standingvals = ('Able/Distinguished/Respected/Master'+ '/Renowned/Grand-Master/Legendary/Ultimate').split('/') +standing_limit = len(standingvals) pirate_ref_re = regexp.compile('^/yoweb/pirate\\.wm') @@ -41,9 +54,12 @@ def debug(m): if opts.debug > 0: print >>opts.debug_file, m -def sleep(seconds): +def debug_flush(): if opts.debug > 0: - opts.debug_file.flush() + opts.debug_file.flush() + +def sleep(seconds): + debug_flush() time.sleep(seconds) def format_time_interval(ti): @@ -248,7 +264,7 @@ u'\\s*\\S*/([-A-Za-z]+)\\s*$|\\s*\\S*/\\S*\\s*\\(ocean\\-wide(?:\\s|\\xa0)+([-A- skl.msg('puzzle "%s" no standing found' % puzzle) continue standing = sl[0] - for i in range(0, len(standingvals)): + for i in range(0, standing_limit): if standing == standingvals[i]: self.standings[puzzle] = i if not puzzle in self.standings: @@ -335,14 +351,7 @@ class StandingsTable: def __init__(self, use_puzzles=None, col_width=6): if use_puzzles is None: if opts.ship_duty: - use_puzzles=[ - 'Navigating','Battle Navigation', - 'Gunning', - ['Sailing','Rigging'], - 'Bilging', - 'Carpentry', - 'Treasure Haul' - ] + use_puzzles=duty_puzzles else: use_puzzles=puzzles self._puzzles = use_puzzles @@ -835,6 +844,8 @@ class ChatLogTracker: # sorted by pirate name # you can pass this None and you'll get [] # or True for the current vessel (which is the default) + # the returned value is a fresh list of persistent + # PirateAboard objects if vesselname is True: v = self._v else: v = self._vl.get(vesselname.title()) if v is None: return [] @@ -989,16 +1000,56 @@ def do_ship_aid(args, bu): if opts.ship_duty is None: opts.ship_duty = True displayer = globals()['Display_'+opts.display]() - rotate_nya = '/-\\' (myself, track) = prep_chat_log(args, bu, progress=displayer) - def timeevent(t,e): - if t is None: return ' ' * 22 - return " %-4s %-16s" % (format_time_interval(now - t),e) - displayer.realstart() + if os.isatty(0): kr_create = KeystrokeReader + else: kr_create = DummyKeystrokeReader + + try: + kreader = kr_create(0, 10) + ship_aid_core(myself, track, displayer, kreader) + finally: + kreader.stop() + print '\n' + +class KeyBasedSorter: + def compar_key_pa(self, pa): + return self.compar_key(pa.pirate_info()) + def lsort_pa(self, l): + l.sort(key = self.compar_key_pa) + +class NameSorter(KeyBasedSorter): + def compar_key(self, pi): return pi.name + +class SkillSorter(NameSorter): + def __init__(self, relevant): + self._want = frozenset(relevant.split('/')) + self._avoid = set() + for p in core_duty_puzzles: + if isinstance(p,basestring): self._avoid.add(p) + else: self._avoid |= set(p) + self._avoid -= self._want + + def compar_key(self, pi): + best_want = max([ + pi.standings.get(puz,-1) + for puz in self._want + ]) + best_avoid = [ + -pi.standings.get(puz,standing_limit) + for puz in self._avoid + ] + best_avoid.sort() + def negate(x): return -x + debug('compar_key %s bw=%s ba=%s' % (pi.name, `best_want`, + `best_avoid`)) + return (-best_want, map(negate, best_avoid), pi.name) + +def ship_aid_core(myself, track, displayer, kreader): + def find_vessel(): vn = track.vesselname() if vn: return (vn, " on board the %s" % vn) @@ -1006,21 +1057,33 @@ def do_ship_aid(args, bu): if vn: return (vn, " ashore from the %s" % vn) return (None, " not on a vessel") + def timeevent(t,e): + if t is None: return ' ' * 22 + return " %-4s %-16s" % (format_time_interval(now - t),e) + displayer.show(track.myname() + find_vessel()[1] + '...') + rotate_nya = '/-\\' + + sort = NameSorter() + while True: track.catchup() now = time.time() (vn, s) = find_vessel() s = track.myname() + s - s += " at %s\n" % time.strftime("%Y-%m-%d %H:%M:%S") + s += " at %s" % time.strftime("%Y-%m-%d %H:%M:%S") + s += kreader.info() + s += '\n' tbl = StandingsTable() tbl.headings() aboard = track.aboard(vn) + sort.lsort_pa(aboard) + for pa in aboard: pi = pa.pirate_info() @@ -1036,10 +1099,51 @@ def do_ship_aid(args, bu): tbl.pirate(pi, xs) s += tbl.results() - displayer.show(s) - sleep(1) - rotate_nya = rotate_nya[1:3] + rotate_nya[0] + + k = kreader.getch() + if k is None: + rotate_nya = rotate_nya[1:3] + rotate_nya[0] + continue + + if k == 'q': break + elif k == 'g': sort = SkillSorter('Gunning') + elif k == 'c': sort = SkillSorter('Carpentry') + elif k == 's': sort = SkillSorter('Sailing/Rigging') + elif k == 'b': sort = SkillSorter('Bilging') + elif k == 'n': sort = SkillSorter('Navigating') + elif k == 'd': sort = SkillSorter('Battle Navigation') + elif k == 't': sort = SkillSorter('Treasure Haul') + elif k == 'a': sort = NameSorter() + else: pass # unknown key command + +#---------- individual keystroke input ---------- + +class DummyKeystrokeReader: + def __init__(self,fd,timeout_dummy): pass + def stop(self): pass + def getch(self): sleep(1); return None + def info(self): return ' [noninteractive]' + +class KeystrokeReader(DummyKeystrokeReader): + def __init__(self, fd, timeout_decisec=0): + self._fd = fd + self._saved = termios.tcgetattr(fd) + a = termios.tcgetattr(fd) + a[3] &= ~(termios.ECHO | termios.ECHONL | + termios.ICANON | termios.IEXTEN) + a[6][termios.VMIN] = 0 + a[6][termios.VTIME] = timeout_decisec + termios.tcsetattr(fd, termios.TCSANOW, a) + def stop(self): + termios.tcsetattr(self._fd, termios.TCSANOW, self._saved) + def getch(self): + debug_flush() + byte = os.read(self._fd, 1) + if not len(byte): return None + return byte + def info(self): + return '' #---------- main program ----------