├── .gitignore ├── LICENSE ├── README.md ├── ncurses └── ncurses.py ├── script ├── README.md ├── accessdb.py ├── accountz.py ├── ascmini.py ├── asyncredis.py ├── blockchain.py ├── bncread.py ├── cavemake.py ├── cfs.py ├── cheat.py ├── cloudclip.py ├── compinit.py ├── crond.py ├── dosbox.py ├── echosrv.py ├── emake.py ├── escope.py ├── fasd.py ├── fiction_mangg.py ├── fiction_zhuaji.py ├── field256.py ├── filesync2.py ├── fzf_cd.py ├── gamelevel.py ├── gobang.py ├── googauth.py ├── incparse.py ├── intel2gas.py ├── kanaquiz.py ├── linguist.py ├── literotica.py ├── playmp3.py ├── pocketsnes_cht.py ├── readkey.py ├── readkrc.py ├── shell.py ├── stardict.py ├── tcjump.py ├── terminal.py ├── winamp.py └── youdao.py └── vintage ├── apihook ├── apihook.c └── apihook.h ├── asmpure ├── README.md ├── asmpure.c └── asmpure.h ├── bitmap ├── BasicBitmap.cpp ├── BasicBitmap.h ├── ibitmap.c └── ibitmap.h ├── context ├── README.md ├── contextu.c ├── contextu.h ├── contextu_msvc_x64.asm ├── test_ctx.cpp └── test_swap.cpp ├── cprofile ├── README.md ├── cprofile.c ├── cprofile.h ├── test.cpp └── test_c.c └── lzw ├── ilzw.c └── ilzw.h /.gitignore: -------------------------------------------------------------------------------- 1 | *.exe 2 | *.ini 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 Linwei 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Collection 2 | 3 | 无处安放的代码,懒得开那么多项目了,以后乱七八糟的东西放这里吧。 4 | 5 | ## Content 6 | 7 | | Folder | Description | 8 | |--------|-------------| 9 | | [script](https://github.com/skywind3000/collection/tree/master/script) | 好玩的 python 代码集合 | 10 | | [apihook](https://github.com/skywind3000/collection/tree/master/vintage/apihook) | 简易 Win32 API HOOK | 11 | | [cprofile](https://github.com/skywind3000/collection/tree/master/vintage/cprofile) | 代码性能测试工具 | 12 | | [asmpure](https://github.com/skywind3000/collection/tree/master/vintage/asmpure) | 实时解析 x86 汇编并生成调用函数 (JIT) | 13 | | [lzw](https://github.com/skywind3000/collection/tree/master/vintage/lzw) | lzw/lzo 压缩/解压 | 14 | | [context](https://github.com/skywind3000/collection/tree/master/vintage/context) | 上下文切换(x86/x64),coroutine 系统的后端 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /ncurses/ncurses.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | #====================================================================== 4 | # 5 | # ncurses.py - 6 | # 7 | # Download curses pyd on windows from: 8 | # https://www.lfd.uci.edu/~gohlke/pythonlibs/#curses 9 | # 10 | # Created by skywind on 2018/02/02 11 | # Last change: 2018/02/02 00:11:36 12 | # 13 | #====================================================================== 14 | from __future__ import print_function 15 | import sys 16 | import time 17 | import os 18 | import curses 19 | import curses.panel 20 | import curses.textpad 21 | 22 | 23 | #---------------------------------------------------------------------- 24 | # basic curses 25 | #---------------------------------------------------------------------- 26 | class ncurses (object): 27 | 28 | def __init__ (self): 29 | self.name = 'ncurses' 30 | self.screen = None 31 | self.guard = False 32 | 33 | def init (self): 34 | self.screen = curses.initscr() 35 | # curses.def_shell_mode() 36 | curses.noecho() 37 | curses.cbreak() 38 | self.screen.keypad(1) 39 | try: 40 | curses.start_color() 41 | except: 42 | pass 43 | return 0 44 | 45 | def quit (self): 46 | if self.screen: 47 | self.screen.keypad(0) 48 | self.screen = None 49 | curses.echo() 50 | curses.nocbreak() 51 | curses.endwin() 52 | # curses.reset_shell_mode() 53 | return 0 54 | 55 | def wrapper (self, main, *args, **argv): 56 | hr = 0 57 | if not self.guard: 58 | self.init() 59 | hr = main(self.screen, *args, **argv) 60 | self.quit() 61 | else: 62 | try: 63 | self.init() 64 | hr = main(self.screen, *args, **argv) 65 | finally: 66 | self.quit() 67 | return hr 68 | 69 | #---------------------------------------------------------------------- 70 | # log out 71 | #---------------------------------------------------------------------- 72 | def plog(*args): 73 | try: 74 | now = time.strftime('%Y-%m-%d %H:%M:%S') 75 | date = now.split(None, 1)[0].replace('-', '') 76 | logfile = sys.modules[__name__].__dict__.get('logfile', None) 77 | logtime = sys.modules[__name__].__dict__.get('logtime', '') 78 | loghome = sys.modules[__name__].__dict__.get('loghome', '') 79 | if not loghome: 80 | loghome = os.getcwd() 81 | sys.modules[__name__].__dict__['loghome'] = loghome 82 | if date != logtime: 83 | logtime = date 84 | if logfile: logfile.close() 85 | logfile = None 86 | if logfile == None: 87 | logname = '%s%s.log'%('c', date) 88 | logfile = open(os.path.join(loghome, logname), 'a') 89 | sys.modules[__name__].__dict__['logtime'] = logtime 90 | sys.modules[__name__].__dict__['logfile'] = logfile 91 | def strlize(x): 92 | if isinstance(x, unicode): 93 | return x.encode("utf-8") 94 | return str(x) 95 | str_args = map(strlize, args) 96 | text = " ".join(str_args) 97 | logfile.write('[%s] %s\n'%(now, text)) 98 | logfile.flush() 99 | if 1 == 0: 100 | text = '[%s] %s\n'%(now, text) 101 | #STDOUT.write(text.decode('utf-8')) 102 | stdout.write(text) 103 | except Exception: 104 | pass 105 | return 0 106 | 107 | 108 | #---------------------------------------------------------------------- 109 | # testing 110 | #---------------------------------------------------------------------- 111 | if __name__ == '__main__': 112 | 113 | nc = ncurses() 114 | os.environ['PDC_RESTORE_SCREEN'] = 'yes' 115 | 116 | def test1(): 117 | def main(scr): 118 | scr.border() 119 | while True: 120 | print('fuck') 121 | ch = scr.getch() 122 | if ch == ord('q'): 123 | break 124 | if ch == curses.KEY_RESIZE: 125 | y, x = scr.getmaxyx() 126 | print('resize %dx%d'%(x, y)) 127 | return 0 128 | print('hello') 129 | nc.wrapper(main) 130 | print('end') 131 | return 0 132 | 133 | def test2(): 134 | def main(stdscr): 135 | begin_x = 20 136 | begin_y = 7 137 | height = 5 138 | width = 40 139 | win = curses.newwin(height, width, begin_y, begin_x) 140 | tb = curses.textpad.Textbox(win) 141 | text = tb.edit() 142 | curses.addstr(4,1,text.encode('utf_8')) 143 | time.sleep(10) 144 | nc.wrapper(main) 145 | 146 | test1() 147 | 148 | 149 | -------------------------------------------------------------------------------- /script/README.md: -------------------------------------------------------------------------------- 1 | ## Script 2 | 3 | 好玩的 Python 脚本 4 | 5 | | Name | Introduce | 6 | |------|-----------| 7 | | shell.py | 命令行相关 (执行命令,捕获输出, 请求url, 等) | 8 | | [crond.py](https://github.com/skywind3000/collection/wiki/Script#crondpy) | Crontab 的 python 实现,当你需要一个独立的 crontab 系统时你会需要它;或者你的嵌入式 linux/nas 系统不允许你编辑默认 crontab 的时候,它会十分顺手,再或者你仅仅需要再 Windows 下有一个 crontab | 9 | | echosvr.py | UDP 性能测试脚本 | 10 | | dosbox.py | 简单的调度 dosbox 执行测试程序的脚本 | 11 | | emake.py | 项目构建工具 | 12 | | intel2gas.py | 转换 Intel 格式的汇编到 AT&T 格式 | 13 | | escope.py | 简单的 gtags 命令行封装,提供类似 cscope的接口,并将数据文件放到 ~/.local/escope/... 下边,避免数据文件污染项目目录 | 14 | | [gobang.py](https://github.com/skywind3000/collection/wiki/Script#youdaopy) | 命令行版本的五子棋人机对战,只有 900行 | 15 | | readkey.py | 终端键盘码测试程序, Ctrl-\ to exit | 16 | | [terminal.py](https://github.com/skywind3000/collection/wiki/Script#terminalpy) | 打开一个新的终端窗口执行命令, 支持 windows/cygwin/ubuntu/mac osx | 17 | | [youdao.py](https://github.com/skywind3000/collection/wiki/Script#youdaopy) | 有道词典本地数据库查询 (需要有道 4.4 的 `dicten.db`/`dictcn.db` 两个词库文件). 同时支持文曲星的 `.LMS`/`.GVD` 文件. 可以给文曲星导出 `.WTB` 单词表 (WeiQuXing Word List). | 18 | | asyncredis.py | 异步 redis 客户端,递进解析 redis 命令 | 19 | | [cavemake.py](https://github.com/skywind3000/collection/wiki/Script#cavemakepy) | 地图生成 | 20 | | [gamelevel.py](https://github.com/skywind3000/collection/wiki/Script#gamelevel) | 关卡生成 | 21 | | cfs.py | 分布式文件系统,使用 mongodb + memcached(可选)作为后端 | 22 | | incparse.py | 分析 C/C++ 代码,找出头文件依赖列表 | 23 | | winamp.py | 使用 ctypes 播放音乐,需要 winamp 的 in_mp3.dll/out_wave.dll 两个文件 | 24 | | playmp3.py | 命令行 mp3播放,使用 ctypes 和 win32 api `mciSendString` 作为后端 | 25 | | readkrc.py | 酷狗 lyrics (.krc) 歌词解密 | 26 | | fiction_mangg.py | http://www.mangg.com (追书网小说下载) | 27 | | fiction_zhuaji.py | http://www.zhuaji.org (爪机书屋下载) | 28 | -------------------------------------------------------------------------------- /script/accessdb.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skywind3000/collection/add7802471ed96839312b1e917263beb08bed2c4/script/accessdb.py -------------------------------------------------------------------------------- /script/blockchain.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | #====================================================================== 4 | # 5 | # blockchain.py - 6 | # 7 | # Created by skywind on 2018/03/08 8 | # Last Modified: 2018/03/08 18:08:29 9 | # 10 | #====================================================================== 11 | from __future__ import print_function 12 | import sys 13 | import time 14 | import hashlib 15 | import json 16 | 17 | 18 | #---------------------------------------------------------------------- 19 | # Blockchain 20 | #---------------------------------------------------------------------- 21 | class Blockchain(object): 22 | 23 | def __init__ (self): 24 | self.chain = [] 25 | self.current_transactions = [] 26 | self.new_block(previous_hash = 1, proof = 100) 27 | 28 | def new_block (self, proof, previous_hash = None): 29 | block = { 30 | 'index': len(self.chain) + 1, 31 | 'timestamp': time.time(), 32 | 'transactions': self.current_transactions, 33 | 'proof': 'proof', 34 | 'previous_hash': previous_hash or self.hash(self.chain[-1]), 35 | } 36 | self.current_transactions = [] 37 | self.chain.append(block) 38 | return block 39 | 40 | def new_transaction (self, sender, recipient, amount): 41 | self.current_transactions.append({ 42 | 'sender': sender, 43 | 'recipient': recipient, 44 | 'amount': amount, 45 | }) 46 | if not self.chain: 47 | return 1 48 | return self.last_block['index'] + 1 49 | 50 | @staticmethod 51 | def hash (block): 52 | block_string = json.dumps(block, sort_keys = True).encode() 53 | return hashlib.sha256(block_string).hexdigest() 54 | 55 | @property 56 | def last_block (self): 57 | if not self.chain: 58 | return None 59 | return self.chain[-1] 60 | 61 | -------------------------------------------------------------------------------- /script/bncread.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | #====================================================================== 4 | # 5 | # bncread.py - 6 | # 7 | # Created by skywind on 2017/03/27 8 | # Last change: 2017/03/27 12:26:48 9 | # 10 | #====================================================================== 11 | import sys 12 | import os 13 | import time 14 | import csv 15 | 16 | sys.path.append('e:/lab/web/common') 17 | import ascmini 18 | 19 | 20 | 21 | #---------------------------------------------------------------------- 22 | # WordStem 23 | #---------------------------------------------------------------------- 24 | class WordStem (object): 25 | def __init__ (self, stem): 26 | self.stem = stem 27 | self.count = 0 28 | self.words = {} 29 | def add (self, c5, word, n = 1): 30 | if c5 and word: 31 | term = (c5, word) 32 | if not term in self.words: 33 | self.words[term] = n 34 | else: 35 | self.words[term] += n 36 | self.count += 1 37 | return True 38 | def dump (self): 39 | output = [] 40 | for term in self.words: 41 | c5, word = term 42 | output.append((c5, word, self.words[term])) 43 | output.sort(key = lambda x: x[2], reverse = True) 44 | return output 45 | 46 | 47 | #---------------------------------------------------------------------- 48 | # 49 | #---------------------------------------------------------------------- 50 | class WordStatistic (object): 51 | def __init__ (self): 52 | self.words = {} 53 | 54 | def load (self, filename): 55 | rows = ascmini.csv_load(filename) 56 | if not rows: 57 | return False 58 | words = self.words 59 | for row in rows: 60 | if not row: 61 | continue 62 | if len(row) < 4: 63 | continue 64 | stem = row[0].lower() 65 | c5 = row[1] 66 | word = row[3] 67 | if not word: 68 | continue 69 | if not stem in words: 70 | ws = WordStem(stem) 71 | words[stem] = ws 72 | else: 73 | ws = words[stem] 74 | ws.add(c5, word.lower()) 75 | rows = None 76 | return True 77 | 78 | def save (self, filename): 79 | words = [ self.words[s] for s in self.words ] 80 | words.sort(key = lambda x: x.count, reverse = True) 81 | output = [] 82 | for stem in words: 83 | row = [stem.stem, stem.count] 84 | for record in stem.dump(): 85 | output.append(row + list(record)) 86 | ascmini.csv_save(output, filename) 87 | return True 88 | 89 | def __len__ (self): 90 | return len(self.words) 91 | 92 | 93 | #---------------------------------------------------------------------- 94 | # 95 | #---------------------------------------------------------------------- 96 | class BncReader (object): 97 | 98 | def __init__ (self): 99 | pass 100 | 101 | def read_bnc_text (self, filename): 102 | # import xml.etree.ElementTree as ET 103 | import lxml.etree 104 | root = lxml.etree.parse(filename) 105 | output = [] 106 | for p in root.iterfind('.//w'): 107 | hw = p.attrib.get('hw', '') 108 | c5 = p.attrib.get('c5', '') 109 | pos = p.attrib.get('pos', '') 110 | word = p.text and p.text.strip() or '' 111 | output.append((hw, c5, pos, word)) 112 | return output 113 | 114 | def search_xmls (self, name): 115 | import fnmatch 116 | matches = [] 117 | for root, dirnames, filenames in os.walk(name): 118 | for filename in fnmatch.filter(filenames, '*.xml'): 119 | matches.append(os.path.join(root, filename)) 120 | return matches 121 | 122 | def dump_bnc (self): 123 | lines = 0 124 | index = 0 125 | for cwd in os.listdir('.'): 126 | if len(cwd) != 1 or (not cwd.isupper()): 127 | continue 128 | xmls = self.search_xmls(cwd) 129 | writer = None 130 | count = 0 131 | fp = None 132 | for xmlname in xmls: 133 | rows = self.read_bnc_text(xmlname) 134 | for row in rows: 135 | if writer is None: 136 | index += 1 137 | name = 'output/bnc-%s-%d.csv'%(cwd.lower(), index) 138 | fp = open(name, 'w') 139 | writer = csv.writer(fp) 140 | print 'swith to', name 141 | writer.writerow([ n.encode('utf-8') for n in row ]) 142 | lines += 1 143 | if lines % 2000000 == 0: 144 | if fp: fp.close() 145 | fp = None 146 | writer = None 147 | count += 1 148 | print '%s: %d/%d'%(name, count, len(xmls)) 149 | return True 150 | 151 | def statistic (self): 152 | ws = WordStatistic() 153 | for filename in ascmini.posix.find_files('words-bnc', '*.csv'): 154 | print filename, len(ws) 155 | ws.load(filename) 156 | ws.save('output.txt') 157 | 158 | 159 | #---------------------------------------------------------------------- 160 | # main 161 | #---------------------------------------------------------------------- 162 | if __name__ == '__main__': 163 | def test1(): 164 | bnc = BncReader() 165 | # bnc.dump_bnc() 166 | return 0 167 | def test2(): 168 | bnc = BncReader() 169 | bnc.statistic() 170 | return 0 171 | test2() 172 | 173 | 174 | 175 | -------------------------------------------------------------------------------- /script/cavemake.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/python 2 | #====================================================================== 3 | # 4 | # cavemake.py - cavemake routine 5 | # 6 | # HISTORY: 7 | # May.8 2008 skywind - create this file with some basic interface 8 | # May.19 2008 skywind - add file saving interface 9 | # Apr.25 2008 skywind - add updating iterator 10 | # Oct.8 2008 skywind - add faultage level 11 | # Oct.9 2008 skywind - upgrade point choosing method 12 | # 13 | #====================================================================== 14 | 15 | import sys, random 16 | 17 | # to change this 18 | randint = random.randint 19 | 20 | def create_matrix(w = 8, h = 8, c = 0): 21 | m = [ [ c for j in xrange(w) ] for i in xrange(h) ] 22 | return m 23 | 24 | def copy_matrix(m): 25 | return [ [ n for n in line ] for line in m ] 26 | 27 | def matrix_size(m): 28 | if len(m) == 0: return 0, 0 29 | return len(m[0]), len(m) 30 | 31 | def copy_to(dst, src): 32 | w, h = matrix_size(src) 33 | for j in xrange(h): 34 | for i in xrange(w): 35 | dst[j][i] = src[j][i] 36 | 37 | def print_matrix(m, is_all_char = False): 38 | mlen = 0 39 | for line in m: 40 | for n in line: 41 | s = len(str(n)) 42 | mlen = max(mlen, s) 43 | for line in m: 44 | text = '' 45 | for n in line: 46 | result = str(n).rjust(mlen) + ' ' 47 | if is_all_char: result = str(n)[:1] 48 | text += result 49 | print text 50 | print '' 51 | 52 | 53 | class disjointset: 54 | def __init__(self): 55 | self.__father = {} 56 | self.__weight = {} 57 | self.__size = 0 58 | def __len__(self): 59 | return self.__size 60 | def find(self, x): 61 | if x == None: 62 | return None 63 | father = self.__father 64 | if x in father: 65 | root = x 66 | path = [] 67 | while father[root] != None: 68 | path.append(root) 69 | root = father[root] 70 | for n in path: 71 | self.__father[n] = root 72 | return root 73 | self.__father[x] = None 74 | self.__weight[x] = 1 75 | self.__size += 1 76 | return x 77 | def __getitem__(self, key): 78 | return self.find(key) 79 | def weight(self, x): 80 | return self.__weight[self.find(x)] 81 | def clear(self): 82 | self.__father = {} 83 | self.__weight = {} 84 | self.__size = 0 85 | def union(self, x, y): 86 | root1 = self.find(x) 87 | root2 = self.find(y) 88 | if root1 != root2: 89 | if self.__weight[root1] < self.__weight[root2]: 90 | self.__weight[root2] += self.__weight[root1] 91 | del self.__weight[root1] 92 | self.__father[root1] = root2 93 | else: 94 | self.__weight[root1] += self.__weight[root2] 95 | del self.__weight[root2] 96 | self.__father[root2] = root1 97 | def split(self): 98 | roots = {} 99 | for n in self.__father: 100 | f = self.find(n) 101 | if f in roots: 102 | roots[f].append(n) 103 | else: 104 | roots[f] = [n] 105 | return roots 106 | 107 | class simplebunch: 108 | def __init__ (self, **kwds): self.__dict__ = kwds 109 | 110 | lengthof = lambda p1, p2: ((p1[0] - p2[0]) ** 2) + ((p1[1] - p2[1]) ** 2) 111 | 112 | INCX = (0, 1, 1, 1, 0, -1, -1, -1) 113 | INCY = (-1, -1, 0, 1, 1, 1, 0, -1) 114 | 115 | CPERMWALL = 100 116 | CWALL = 110 117 | CFLOOR = 200 118 | 119 | class cavemake: 120 | def __init__(self, width = 32, height = 20, initial = 0.4): 121 | self.__width = width 122 | self.__height = height 123 | self.__initial = initial 124 | self.__map = [ [0 for i in xrange(width)] for j in xrange(height) ] 125 | self.__ds = disjointset() 126 | self.edges = [] 127 | self.roomset = [] 128 | 129 | def clear(self): 130 | m = self.__map 131 | for j in xrange(self.__height): 132 | for i in xrange(self.__width): 133 | m[j][i] = CWALL 134 | for j in xrange(self.__height): 135 | m[j][0] = CPERMWALL 136 | m[j][self.__width - 1] = CPERMWALL 137 | for i in xrange(self.__width): 138 | m[0][i] = CPERMWALL 139 | m[self.__height - 1][i] = CPERMWALL 140 | self.roomset = [] 141 | self.edges = [] 142 | self.__ds.clear() 143 | 144 | def print_cave(self): 145 | m = self.__map 146 | for j in xrange(self.__height): 147 | row = '' 148 | for n in m[j]: 149 | if n == CFLOOR: row += '.' 150 | elif n == CWALL or n == CPERMWALL: row += '#' 151 | elif (n >= 0) and (n <= 9): row += str(n) 152 | else: row += 'X' 153 | 154 | print row 155 | print 156 | 157 | def __getitem__ (self, row): 158 | if (row < 0) or (row >= self.__height): 159 | raise KeyError("row out of range") 160 | return self.__map[row] 161 | 162 | def __iter__ (self): 163 | return self.__map.__iter__() 164 | 165 | def dump(self): 166 | return self.__map 167 | 168 | def pass_ca(self, no_new_wall = False, keep = False, use_backup = False): 169 | m = self.__map 170 | incx = (0, 1, 1, 1, 0, -1, -1, -1) 171 | incy = (-1, -1, 0, 1, 1, 1, 0, -1) 172 | n = m 173 | if use_backup: 174 | n = [ [ x for x in line ] for line in m ] 175 | neighbor = [ CFLOOR for x in xrange(8) ] 176 | for y in xrange(1, self.__height - 1): 177 | for x in xrange(1, self.__width - 1): 178 | wall_count = 0 179 | for d in xrange(8): 180 | neighbor[d] = n[y + incy[d]][x + incx[d]] 181 | if neighbor[d] != CFLOOR: 182 | wall_count += 1 183 | adjacence = 0 184 | for d in xrange(8): 185 | if neighbor[d] == CFLOOR: break 186 | if d < 8: 187 | for step in xrange(8): 188 | if neighbor[(d + step) & 7] != CFLOOR: 189 | break 190 | adjacence += 1 191 | canwall = False 192 | if (adjacence + wall_count == 8) or (not keep): 193 | canwall = True 194 | if (wall_count < 4) and (m[y][x] == CWALL): 195 | m[y][x] = CFLOOR 196 | elif (wall_count > 5) and (m[y][x] == CFLOOR): 197 | if (not no_new_wall) and canwall: 198 | m[y][x] = CWALL 199 | 200 | def initialize(self): 201 | m = self.__map 202 | count = int(self.__width * self.__height * self.__initial) 203 | maxcount = self.__width * self.__height * 2 204 | self.clear() 205 | while count > 0: 206 | x = randint(1, self.__width - 2) 207 | y = randint(1, self.__height - 2) 208 | if m[y][x] == CWALL: 209 | m[y][x] = CFLOOR 210 | count -= 1 211 | maxcount -= 1 212 | if maxcount <= 0: 213 | break 214 | 215 | def __search_rooms(self): 216 | ds = self.__ds 217 | m = self.__map 218 | ds.clear() 219 | for y in xrange(1, self.__height - 1): 220 | for x in xrange(1, self.__width - 1): 221 | if m[y][x] != CFLOOR: 222 | continue 223 | root = ds[(y, x)] 224 | if m[y][x + 1] == CFLOOR: 225 | ds.union(root, (y, x + 1)) 226 | if m[y + 1][x] == CFLOOR: 227 | ds.union(root, (y + 1, x)) 228 | if m[y + 1][x + 1] == CFLOOR: 229 | if (m[y][x + 1] == CFLOOR) or (m[y + 1][x] == CFLOOR): 230 | ds.union(root, (y + 1, x + 1)) 231 | if m[y + 1][x - 1] == CFLOOR: 232 | if (m[y][x - 1] == CFLOOR) or (m[y + 1][x] == CFLOOR): 233 | ds.union(root, (y + 1, x - 1)) 234 | 235 | def __choose_dir(self, src, dest, noisy = True): 236 | if src[0] < dest[0]: incy = 1 237 | elif src[0] > dest[0]: incy = -1 238 | else: incy = 0 239 | if src[1] < dest[1]: incx = 1 240 | elif src[1] > dest[1]: incx = -1 241 | else: incx = 0 242 | if (noisy) and (incx != 0) and (incy != 0): 243 | n = randint(0, 1) 244 | if n == 0: incx = 0 245 | elif n == 1: incy = 0 246 | return incy, incx 247 | 248 | def __join_room(self, pt, destpt): 249 | ds = self.__ds 250 | m = self.__map 251 | pair = None 252 | while True: 253 | incy, incx = self.__choose_dir(pt, destpt, True) 254 | npt = (pt[0] + incy, pt[1] + incx) 255 | root = ds[pt] 256 | need_stop = False 257 | if npt == destpt: 258 | need_stop = True 259 | pair = (root, ds[destpt]) 260 | elif m[npt[0]][npt[1]] == CFLOOR: 261 | if ds[npt] != root: 262 | pair = (root, ds[npt]) 263 | need_stop = True 264 | m[npt[0]][npt[1]] = CFLOOR 265 | ds.union(root, ds[npt]) 266 | if randint(0, 1) == 0: 267 | r = randint(0, 7) 268 | noisy = pt[0] + INCY[r], pt[1] + INCX[r] 269 | if m[noisy[0]][noisy[1]] == CWALL: 270 | m[noisy[0]][noisy[1]] = CFLOOR 271 | ds.union(root, ds[noisy]) 272 | pass 273 | pt = npt 274 | if need_stop: break 275 | return pair 276 | 277 | def __update_rooms(self): 278 | self.__search_rooms() 279 | sets = self.__ds.split() 280 | roomset = {} 281 | for n in sets: 282 | room = simplebunch(root = n, pts = sets[n], size=0) 283 | xmin, ymin = self.__width, self.__height 284 | xmax, ymax = 0, 0 285 | for y, x in sets[n]: 286 | if x < xmin: xmin = x 287 | if x > xmax: xmax = x 288 | if y < ymin: ymin = y 289 | if y > ymax: ymax = y 290 | cx = (xmin + xmax + 1) * 0.5 291 | cy = (ymin + ymax + 1) * 0.5 292 | best_dist = (self.__width + self.__height) ** 3 293 | best_pt = room.root 294 | for pt in sets[n]: 295 | dist = (cy - pt[0]) ** 2 + (cx - pt[1]) ** 2 296 | if dist < best_dist: 297 | best_dist, best_pt = dist, pt 298 | room.center = best_pt 299 | room.size = len(room.pts) 300 | #print room.center, (cy, cx), room.root 301 | roomset[n] = room 302 | return roomset 303 | 304 | def __choose_entrance(self): 305 | roomset = self.__update_rooms() 306 | border = [] 307 | for i in xrange(1, self.__width - 1): 308 | border.append((1, i)) 309 | border.append((self.__height - 2, i)) 310 | for i in xrange(1, self.__height - 1): 311 | border.append((i, 1)) 312 | border.append((i, self.__width - 2)) 313 | dists = [] 314 | for pt in border: 315 | dist_sum = 0 316 | for key in roomset: 317 | room = roomset[key] 318 | dist_sum += lengthof(room.center, pt) 319 | dists.append((dist_sum, pt)) 320 | dists.sort() 321 | dists.reverse() 322 | if len(dists) < 4: return dists[0][1] 323 | pos = len(dists) 324 | pos = randint(0, int(pos / 2)) 325 | return dists[pos][1] 326 | 327 | def open_area(self, entrance): 328 | for incy in (-1, 0, 1): 329 | for incx in (-1, 0, 1): 330 | y = entrance[0] + incy 331 | x = entrance[1] + incx 332 | #if abs(incx) + abs(incy) >= 2: 333 | # if randint(0, 1) == 0: continue 334 | if (y < 0) or (y >= self.__height): continue 335 | if (x < 0) or (x >= self.__width): continue 336 | self.__map[y][x] = CFLOOR 337 | 338 | def generate(self, printraw = False, FLOORLST = [], iter = 0): 339 | self.initialize() 340 | #self.print_cave() 341 | self.pass_ca(use_backup = False) 342 | #self.print_cave() 343 | 344 | entrance = self.__choose_entrance() 345 | entrance = self.__height - 2, int(self.__width * 3 / 4) 346 | self.entrance = entrance 347 | #self.__map[entrance[0]][entrance[1]] = CFLOOR 348 | 349 | try: 350 | for y, x in FLOORLST: 351 | self.__map[y][x] = CFLOOR 352 | except: pass 353 | 354 | if printraw: 355 | self.print_cave() 356 | 357 | m_original = copy_matrix(self.__map) 358 | m_result = copy_matrix(self.__map) 359 | 360 | count = 4; 361 | 362 | for ii in xrange(iter + 1): 363 | self.clear() 364 | copy_to(self.__map, m_original) 365 | self.roomset = self.__update_rooms() 366 | 367 | while True: 368 | roomset = self.__update_rooms() 369 | if len(roomset) <= 1: break 370 | roomlst = roomset.keys() 371 | pair_list = [] 372 | for i in xrange(len(roomlst) - 1): 373 | rooma = roomset[roomlst[i]] 374 | for j in xrange(i + 1, len(roomlst)): 375 | roomb = roomset[roomlst[j]] 376 | dist = lengthof(rooma.center, roomb.center) 377 | pair_list.append((dist, (rooma, roomb))) 378 | pair_list.sort() 379 | limit = 500 380 | index = 0 381 | while True: 382 | if index >= len(pair_list) - 1: break 383 | if randint(0, 100) < limit: break 384 | limit += 10 385 | rooma, roomb = pair_list[0][1] 386 | #print rooma.root, roomb.root 387 | #pair = self.__join_room(rooma.root, roomb.root) 388 | point1 = rooma.pts[random.randint(0, rooma.size - 1)] 389 | point2 = roomb.pts[random.randint(0, roomb.size - 1)] 390 | pair = self.__join_room(point1, point2) 391 | self.edges.append(pair) 392 | #self.print_cave() 393 | 394 | #self.__open_entrance(entrance) 395 | #self.pass_ca(True) 396 | m = self.__map 397 | for j in xrange(self.__height): 398 | for i in xrange(self.__width): 399 | if m[j][i] == CFLOOR: 400 | m_result[j][i] = CFLOOR 401 | 402 | copy_to(self.__map, m_result) 403 | #self.pass_ca(False, True) 404 | self.pass_ca(True) 405 | 406 | return 0 407 | 408 | def faultage(self, level = 2, ratio = 0.3): 409 | m_original = copy_matrix(self.__map) 410 | level = level < 8 and level or 8 411 | for i in xrange(level): 412 | count = 0 413 | m = self.__map 414 | for line in m: 415 | for n in line: 416 | if n == CWALL: count += 1 417 | count = int(count * ratio) 418 | limit = self.__width * self.__height * 3 419 | while (limit > 0) and (count > 0): 420 | x = randint(0, self.__width - 1) 421 | y = randint(0, self.__height - 1) 422 | if m[y][x] in (CWALL, CPERMWALL): 423 | m[y][x] = CFLOOR 424 | count -= 1 425 | limit -= 1 426 | self.pass_ca(use_backup = False) 427 | #self.print_cave() 428 | for y in xrange(self.__height): 429 | for x in xrange(self.__width): 430 | if m_original[y][x] in (CWALL, CPERMWALL): 431 | m_original[y][x] = 1 432 | if m_original[y][x] >= 1 and m_original[y][x] <= 9: 433 | if m[y][x] in (CWALL, CPERMWALL): 434 | m_original[y][x] += 1 435 | elif m_original[y][x] == CFLOOR: 436 | m_original[y][x] = 0 437 | copy_to(self.__map, m_original) 438 | m = self.__map 439 | incx = [ 0, 1, 1, 1, 0, -1, -1, -1 ] 440 | incy = [ -1, -1, 0, 1, 1, 1, 0, -1 ] 441 | incyx = zip(incy, incx) 442 | for j in xrange(0, self.__height): 443 | for i in xrange(0, self.__width): 444 | if m[j][i] in (0, 1): continue 445 | floors = 0 446 | for dy, dx in incyx: 447 | u, v = i + dx, j + dy 448 | if u < 0 or u >= self.__width or \ 449 | v < 0 or v >= self.__height: 450 | floors += 1 451 | continue 452 | if m[v][u] == 0: floors += 1 453 | if floors > 0: m[j][i] = 1 454 | 455 | def test2(self): 456 | self.initialize() 457 | self.pass_ca(use_backup = True) 458 | self.print_cave() 459 | self.__update_rooms() 460 | self.__search_rooms() 461 | rooms = self.__ds.split() 462 | #for n in rooms: print n, self.__ds.weight(n) 463 | n = randint(0, len(rooms.keys()) - 1) 464 | center = rooms.keys()[n] 465 | print 'center', center 466 | for room in rooms: 467 | self.__join_room(rooms[room][0], center) 468 | for i in xrange(1): 469 | self.pass_ca(True, False) 470 | self.print_cave() 471 | 472 | 473 | def cavesave(fp, width, height, ratio = 0.24, entrances = [], iter = 0, \ 474 | fault = 0, fratio = 0.4): 475 | cm = cavemake(width, height, ratio) 476 | cm.generate(False, entrances, iter) 477 | if type(fp) == type(''): 478 | fp = file(fp, 'w') 479 | for e in entrances: 480 | cm.open_area(e) 481 | if fault > 0: 482 | fault = fault <= 8 and fault or 8 483 | cm.faultage(fault, fratio) 484 | for line in cm: 485 | row = '' 486 | for n in line: 487 | if n == CFLOOR or n == 0: row += '.' 488 | elif n == CWALL or n == CPERMWALL: row += '#' 489 | elif (n >= 0) and (n <= 9): row += str(n) 490 | else: 491 | row += '?' 492 | fp.write(row + '\n') 493 | fp.flush() 494 | fp.write('\n') 495 | roomset = cm.roomset 496 | for root in roomset: 497 | room = roomset[root] 498 | center = room.center 499 | fp.write('AREA %d %d\n'%(center[0], center[1])) 500 | for e in entrances: 501 | fp.write('ENTRANCE %d %d\n'%(e[0], e[1])) 502 | fp.flush() 503 | 504 | 505 | try: 506 | import psyco 507 | psyco.bind(disjointset) 508 | psyco.bind(cavemake) 509 | except ImportError: 510 | pass 511 | 512 | 513 | if __name__ == '__main__': 514 | FILE = '-' 515 | WIDTH = 72 516 | HEIGHT = 21 517 | RATIO = 0.24 518 | FAULT = -1 519 | FRATIO = 0.4 520 | FLOORS = '' 521 | ENTRANCE = 0 522 | import time 523 | SEED = int(time.time()) 524 | if len(sys.argv) > 1: FILE = sys.argv[1] 525 | if len(sys.argv) > 2: WIDTH = int(sys.argv[2]) 526 | if len(sys.argv) > 3: HEIGHT = int(sys.argv[3]) 527 | if len(sys.argv) > 4: RATIO = int(sys.argv[4]) * 0.01 528 | if len(sys.argv) > 5: FAULT = int(sys.argv[5]) 529 | if len(sys.argv) > 6: FRATIO = int(sys.argv[6]) * 0.01 530 | if len(sys.argv) > 7: FLOORS = sys.argv[7] 531 | if len(sys.argv) > 8: ENTRANCE = int(sys.argv[8]) 532 | if len(sys.argv) > 9: SEED = int(sys.argv[9]) 533 | if FILE == '-': FILE = sys.stdout 534 | entrance = [] 535 | for pos in FLOORS.split('-'): 536 | try: posy, posx = pos.split(':') 537 | except: continue 538 | posy, posx = int(posy), int(posx) 539 | entrance.append((posy, posx)) 540 | #ENTRANCE = 8 | 4 | 2 | 1 541 | #ENTRANCE = 2 542 | #FAULT = 4 543 | if ENTRANCE & 2: entrance.append((HEIGHT - 2, int(WIDTH * 3 / 4))) 544 | if ENTRANCE & 8: entrance.append((1, int(WIDTH * 1 / 4))) 545 | if ENTRANCE & 1: entrance.append((int(HEIGHT * 1 / 4), WIDTH - 2)) 546 | if ENTRANCE & 4: entrance.append((int(HEIGHT * 3 / 4), 1)) 547 | 548 | import random 549 | random.seed(SEED) 550 | cavesave(FILE, WIDTH, HEIGHT, RATIO, entrance, 0, FAULT, FRATIO) 551 | 552 | 553 | -------------------------------------------------------------------------------- /script/cheat.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | #====================================================================== 4 | # 5 | # cheat.py - python cheat sheet 6 | # 7 | # Created by skywind on 2018/01/25 8 | # Last change: 2018/01/25 16:20:27 9 | # 10 | #====================================================================== 11 | from __future__ import print_function 12 | import sys 13 | import time 14 | import os 15 | import subprocess 16 | import shutil 17 | 18 | 19 | #---------------------------------------------------------------------- 20 | # CheatUtils 21 | #---------------------------------------------------------------------- 22 | class CheatUtils (object): 23 | 24 | def __init__ (self): 25 | self.name = 'utils' 26 | self.isatty = sys.stdout.isatty() 27 | 28 | def colorize (self, sheet_content): 29 | """ Colorizes cheatsheet content if so configured """ 30 | 31 | # only colorize if so configured 32 | if not 'CHEATCOLORS' in os.environ: 33 | return sheet_content 34 | 35 | try: 36 | from pygments import highlight 37 | from pygments.lexers import get_lexer_by_name 38 | from pygments.formatters import TerminalFormatter 39 | 40 | # if pygments can't load, just return the uncolorized text 41 | except ImportError: 42 | return sheet_content 43 | 44 | first_line = sheet_content.splitlines()[0] 45 | lexer = get_lexer_by_name('bash') 46 | if first_line.startswith('```'): 47 | sheet_content = '\n'.join(sheet_content.split('\n')[1:-2]) 48 | try: 49 | lexer = get_lexer_by_name(first_line[3:]) 50 | except Exception: 51 | pass 52 | 53 | return highlight(sheet_content, lexer, TerminalFormatter()) 54 | 55 | def die (self, message): 56 | """ Prints a message to stderr and then terminates """ 57 | warn(message) 58 | exit(1) 59 | 60 | def editor (self): 61 | """ Determines the user's preferred editor """ 62 | 63 | # determine which editor to use 64 | editor = os.environ.get('CHEAT_EDITOR') \ 65 | or os.environ.get('VISUAL') \ 66 | or os.environ.get('EDITOR') \ 67 | or False 68 | 69 | # assert that the editor is set 70 | if editor == False: 71 | die( 72 | 'You must set a CHEAT_EDITOR, VISUAL, or EDITOR environment ' 73 | 'variable in order to create/edit a cheatsheet.' 74 | ) 75 | 76 | return editor 77 | 78 | def open_with_editor (self, filepath): 79 | """ Open `filepath` using the EDITOR specified by the environment variables """ 80 | editor_cmd = self.editor().split() 81 | try: 82 | subprocess.call(editor_cmd + [filepath]) 83 | except OSError: 84 | die('Could not launch ' + self.editor()) 85 | 86 | def warn (self, message): 87 | """ Prints a message to stderr """ 88 | print((message), file=sys.stderr) 89 | 90 | def search_cheat (self): 91 | available = [] 92 | import site 93 | site_system = site.getsitepackages() 94 | site_user = site.getusersitepackages() 95 | for name in [site_user] + site_system: 96 | path = os.path.join(name, 'cheat') 97 | if not os.path.exists(path): 98 | continue 99 | path = os.path.join(path, 'cheatsheets') 100 | if not os.path.exists(path): 101 | continue 102 | available.append(path) 103 | return available 104 | 105 | def set_color (self, color): 106 | if not self.isatty: 107 | return 0 108 | if sys.platform[:3] == 'win': 109 | try: import ctypes 110 | except: return 0 111 | kernel32 = ctypes.windll.LoadLibrary('kernel32.dll') 112 | GetStdHandle = kernel32.GetStdHandle 113 | SetConsoleTextAttribute = kernel32.SetConsoleTextAttribute 114 | GetStdHandle.argtypes = [ ctypes.c_uint32 ] 115 | GetStdHandle.restype = ctypes.c_size_t 116 | SetConsoleTextAttribute.argtypes = [ ctypes.c_size_t, ctypes.c_uint16 ] 117 | SetConsoleTextAttribute.restype = ctypes.c_long 118 | handle = GetStdHandle(0xfffffff5) 119 | if color < 0: color = 7 120 | result = 0 121 | if (color & 1): result |= 4 122 | if (color & 2): result |= 2 123 | if (color & 4): result |= 1 124 | if (color & 8): result |= 8 125 | if (color & 16): result |= 64 126 | if (color & 32): result |= 32 127 | if (color & 64): result |= 16 128 | if (color & 128): result |= 128 129 | SetConsoleTextAttribute(handle, result) 130 | else: 131 | if color >= 0: 132 | foreground = color & 7 133 | background = (color >> 4) & 7 134 | bold = color & 8 135 | sys.stdout.write("\033[%s3%d;4%dm"%(bold and "01;" or "", foreground, background)) 136 | sys.stdout.flush() 137 | else: 138 | sys.stdout.write("\033[0m") 139 | sys.stdout.flush() 140 | return 0 141 | 142 | 143 | #---------------------------------------------------------------------- 144 | # local utils 145 | #---------------------------------------------------------------------- 146 | utils = CheatUtils() 147 | die = utils.die 148 | warn = utils.warn 149 | set_color = utils.set_color 150 | 151 | 152 | #---------------------------------------------------------------------- 153 | # CheatSheets 154 | #---------------------------------------------------------------------- 155 | class CheatSheets (object): 156 | 157 | def __init__ (self): 158 | self.site_sheets = utils.search_cheat() 159 | self.cheats_dict = None 160 | 161 | def default_path (self): 162 | 163 | """ Returns the default cheatsheet path """ 164 | 165 | # determine the default cheatsheet dir 166 | default_sheets_dir = os.environ.get('DEFAULT_CHEAT_DIR') or os.path.join('~', '.cheat') 167 | default_sheets_dir = os.path.expanduser(os.path.expandvars(default_sheets_dir)) 168 | 169 | # create the DEFAULT_CHEAT_DIR if it does not exist 170 | if not os.path.isdir(default_sheets_dir): 171 | try: 172 | # @kludge: unclear on why this is necessary 173 | os.umask(0000) 174 | os.mkdir(default_sheets_dir) 175 | 176 | except OSError: 177 | die('Could not create DEFAULT_CHEAT_DIR') 178 | 179 | # assert that the DEFAULT_CHEAT_DIR is readable and writable 180 | if not os.access(default_sheets_dir, os.R_OK): 181 | die('The DEFAULT_CHEAT_DIR (' + default_sheets_dir +') is not readable.') 182 | if not os.access(default_sheets_dir, os.W_OK): 183 | die('The DEFAULT_CHEAT_DIR (' + default_sheets_dir +') is not writable.') 184 | 185 | # return the default dir 186 | return default_sheets_dir 187 | 188 | def paths (self): 189 | """ Assembles a list of directories containing cheatsheets """ 190 | sheet_paths = [ self.default_path() ] 191 | sheet_paths.extend(self.site_sheets) 192 | 193 | # merge the CHEATPATH paths into the sheet_paths 194 | if 'CHEATPATH' in os.environ and os.environ['CHEATPATH']: 195 | for path in os.environ['CHEATPATH'].split(os.pathsep): 196 | if os.path.isdir(path): 197 | sheet_paths.append(path) 198 | 199 | if not sheet_paths: 200 | die('The DEFAULT_CHEAT_DIR dir does not exist or the CHEATPATH is not set.') 201 | 202 | return sheet_paths 203 | 204 | def get (self): 205 | """ Assembles a dictionary of cheatsheets as name => file-path """ 206 | cheats = {} 207 | 208 | # otherwise, scan the filesystem 209 | for cheat_dir in reversed(self.paths()): 210 | for cheat in os.listdir(cheat_dir): 211 | if cheat[:1] == '.' or cheat[:2] == '__': 212 | continue 213 | cheats[cheat] = os.path.join(cheat_dir, cheat) 214 | 215 | return cheats 216 | 217 | def list (self): 218 | """ Lists the available cheatsheets """ 219 | sheet_list = '' 220 | pad_length = max([len(x) for x in self.get().keys()]) + 4 221 | for sheet in sorted(self.get().items()): 222 | sheet_list += sheet[0].ljust(pad_length) + sheet[1] + "\n" 223 | return sheet_list 224 | 225 | def search (self, term): 226 | """ Searches all cheatsheets for the specified term """ 227 | result = '' 228 | 229 | for cheatsheet in sorted(self.get().items()): 230 | match = '' 231 | for line in open(cheatsheet[1]): 232 | if term in line: 233 | match += ' ' + line 234 | 235 | if match != '': 236 | result += cheatsheet[0] + ":\n" + match + "\n" 237 | 238 | return result 239 | 240 | def sheets (self): 241 | if not self.cheats_dict: 242 | self.cheats_dict = self.get() 243 | return self.cheats_dict 244 | 245 | 246 | #---------------------------------------------------------------------- 247 | # cheatsheets 248 | #---------------------------------------------------------------------- 249 | cheatsheets = CheatSheets() 250 | 251 | 252 | #---------------------------------------------------------------------- 253 | # Sheet 254 | #---------------------------------------------------------------------- 255 | class CheatSheet (object): 256 | 257 | def copy (self, current_sheet_path, new_sheet_path): 258 | """ Copies a sheet to a new path """ 259 | 260 | # attempt to copy the sheet to DEFAULT_CHEAT_DIR 261 | try: 262 | shutil.copy(current_sheet_path, new_sheet_path) 263 | 264 | # fail gracefully if the cheatsheet cannot be copied. This can happen if 265 | # DEFAULT_CHEAT_DIR does not exist 266 | except IOError: 267 | die('Could not copy cheatsheet for editing.') 268 | 269 | def create_or_edit (self, sheet): 270 | """ Creates or edits a cheatsheet """ 271 | 272 | # if the cheatsheet does not exist 273 | if not self.exists(sheet): 274 | self.create(sheet) 275 | 276 | # if the cheatsheet exists but not in the default_path, copy it to the 277 | # default path before editing 278 | elif self.exists(sheet) and not self.exists_in_default_path(sheet): 279 | self.copy(self.path(sheet), os.path.join(cheatsheets.default_path(), sheet)) 280 | self.edit(sheet) 281 | 282 | # if it exists and is in the default path, then just open it 283 | else: 284 | self.edit(sheet) 285 | 286 | def create (self, sheet): 287 | """ Creates a cheatsheet """ 288 | new_sheet_path = os.path.join(cheatsheets.default_path(), sheet) 289 | utils.open_with_editor(new_sheet_path) 290 | 291 | def edit (self, sheet): 292 | """ Opens a cheatsheet for editing """ 293 | utils.open_with_editor(self.path(sheet)) 294 | 295 | def exists (self, sheet): 296 | """ Predicate that returns true if the sheet exists """ 297 | return sheet in cheatsheets.get() and os.access(self.path(sheet), os.R_OK) 298 | 299 | def exists_in_default_path (self, sheet): 300 | """ Predicate that returns true if the sheet exists in default_path""" 301 | default_path_sheet = os.path.join(cheatsheets.default_path(), sheet) 302 | return sheet in cheatsheets.get() and os.access(default_path_sheet, os.R_OK) 303 | 304 | def is_writable (self, sheet): 305 | """ Predicate that returns true if the sheet is writeable """ 306 | return sheet in cheatsheets.get() and os.access(self.path(sheet), os.W_OK) 307 | 308 | def path (self, sheet): 309 | """ Returns a sheet's filesystem path """ 310 | return cheatsheets.get()[sheet] 311 | 312 | def read (self, sheet): 313 | """ Returns the contents of the cheatsheet as a String """ 314 | if not self.exists(sheet): 315 | die('No cheatsheet found for ' + sheet) 316 | 317 | with open(self.path(sheet)) as cheatfile: 318 | return cheatfile.read() 319 | 320 | 321 | #---------------------------------------------------------------------- 322 | # sheet 323 | #---------------------------------------------------------------------- 324 | cheatsheet = CheatSheet() 325 | 326 | 327 | #---------------------------------------------------------------------- 328 | # display text 329 | #---------------------------------------------------------------------- 330 | def display(text): 331 | cheat_colors = os.environ.get('CHEATCOLORS', '') 332 | if cheat_colors and sys.stdout.isatty(): 333 | if cheat_colors in ('0', 'no', 'disable', ''): 334 | print(text) 335 | return 0 336 | if cheat_colors.lower() in ('yes', 'bash', '1'): 337 | if sys.platform[:3] != 'win': 338 | print(utils.colorize(text)) 339 | return 0 340 | else: 341 | print(text) 342 | return 0 343 | colors = [] 344 | if ',' in cheat_colors: 345 | colors = cheat_colors.split(',') 346 | c_main = -1 347 | c_code = 14 348 | c_high = 15 349 | c_comment = 10 350 | if len(colors) > 0 and colors[0].isdigit(): 351 | c_main = int(colors[0]) 352 | if len(colors) > 1 and colors[1].isdigit(): 353 | c_code = int(colors[1]) 354 | if len(colors) > 2 and colors[2].isdigit(): 355 | c_high = int(colors[2]) 356 | if len(colors) > 3 and colors[3].isdigit(): 357 | c_comment = int(colors[3]) 358 | current = c_main 359 | set_color(current) 360 | for line in text.split('\n'): 361 | char = line[:1] 362 | if char.isspace(): 363 | color = c_code 364 | if line.lstrip('\r\n\t ')[:1] == '#': 365 | color = c_comment 366 | elif char == '#': 367 | color = c_comment 368 | elif char == '-': 369 | color = c_high 370 | else: 371 | color = c_main 372 | if color != current: 373 | set_color(color) 374 | current = color 375 | # print('color', color) 376 | print(line) 377 | set_color(-1) 378 | return 0 379 | print(text) 380 | return 0 381 | 382 | 383 | #---------------------------------------------------------------------- 384 | # document 385 | #---------------------------------------------------------------------- 386 | cheatdoc = """cheat 387 | 388 | Create and view cheatsheets on the command line. 389 | 390 | Usage: 391 | cheat 392 | cheat -e 393 | cheat -s 394 | cheat -l 395 | cheat -d 396 | cheat -v 397 | 398 | Options: 399 | -d --directories List directories on CHEATPATH 400 | -e --edit Edit cheatsheet 401 | -l --list List cheatsheets 402 | -s --search Search cheatsheets for 403 | -v --version Print the version number 404 | 405 | Examples: 406 | To view the `tar` cheatsheet: 407 | cheat tar 408 | To edit (or create) the `foo` cheatsheet: 409 | cheat -e foo 410 | To list all available cheatsheets: 411 | cheat -l 412 | To search for "ssh" among all cheatsheets: 413 | cheat -s ssh 414 | """ 415 | 416 | 417 | #---------------------------------------------------------------------- 418 | # usage 419 | #---------------------------------------------------------------------- 420 | def usage(): 421 | print('Usage:') 422 | print(' cheat ') 423 | print(' cheat -e ') 424 | print(' cheat -s ') 425 | print(' cheat -l') 426 | print(' cheat -d') 427 | print(' cheat -v') 428 | return 0 429 | 430 | 431 | #---------------------------------------------------------------------- 432 | # main 433 | #---------------------------------------------------------------------- 434 | def main(args = None): 435 | 436 | args = [ n for n in (args and args or sys.argv) ] 437 | 438 | keywords = ['-e', '--edit', '-s', '--search', '-l', '--list', \ 439 | '-d', '--directories', '-v', '--version', '-h', '--help'] 440 | 441 | if len(args) < 2: 442 | usage() 443 | return 0 444 | 445 | argv, options = [], {} 446 | 447 | for arg in args[1:]: 448 | if arg in keywords: 449 | options[arg] = 1 450 | else: 451 | argv.append(arg) 452 | 453 | word = argv and argv[0] or '' 454 | 455 | if len(argv) > 1: 456 | usage() 457 | return 1 458 | 459 | if '-d' in options or '--directories' in options: 460 | print("\n".join(cheatsheets.paths())) 461 | 462 | elif '-l' in options or '--list' in options: 463 | print(cheatsheets.list()) 464 | 465 | elif '-e' in options or '--edit' in options: 466 | if not word: 467 | usage() 468 | return 1 469 | cheatsheet.create_or_edit(word) 470 | 471 | elif '-h' in options or '--help' in options: 472 | print(cheatdoc) 473 | 474 | elif '-v' in options or '--version' in options: 475 | print('cheat 2.2.3: by Chris Allen Lane and patched by skywind3000') 476 | 477 | elif '-s' in options or '--search' in options: 478 | if not word: 479 | usage() 480 | return 1 481 | text = cheatsheets.search(word) 482 | display(text) 483 | 484 | else: 485 | if not word: 486 | usage() 487 | return 1 488 | text = cheatsheet.read(word) 489 | display(text) 490 | 491 | return 0 492 | 493 | 494 | #---------------------------------------------------------------------- 495 | # testing 496 | #---------------------------------------------------------------------- 497 | if __name__ == '__main__': 498 | 499 | # os.environ['DEFAULT_CHEAT_DIR'] = 'd:/acm/github/vim/cheat' 500 | 501 | def test1(): 502 | print(utils.search_cheat()) 503 | print(1,2,3) 504 | return 0 505 | 506 | def test2(): 507 | print(cheatsheets.default_path()) 508 | import pprint 509 | pprint.pprint(cheatsheets.get()) 510 | return 0 511 | 512 | def test3(): 513 | print(cheatsheet.read('linux')) 514 | usage() 515 | return 0 516 | 517 | def test4(): 518 | os.environ['EDITOR'] = 'vim' 519 | os.environ['CHEATCOLORS'] = 'true' 520 | args = ['tar'] 521 | args = ['-d'] 522 | args = ['-e', 'tar'] 523 | args = ['-s', 'sed'] 524 | args = ['cut'] 525 | # args = ['-v'] 526 | main(sys.argv[:1] + args) 527 | return 0 528 | 529 | # test4() 530 | sys.exit(main()) 531 | 532 | 533 | -------------------------------------------------------------------------------- /script/compinit.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | #====================================================================== 4 | # 5 | # compinit.py - python shell tab completion 6 | # 7 | # Created by skywind on 2018/01/27 8 | # Last change: 2018/01/27 21:48:26 9 | # 10 | #====================================================================== 11 | 12 | def __completion_init(): 13 | try: 14 | import readline 15 | import rlcompleter 16 | import atexit 17 | import os 18 | import sys 19 | except ImportError: 20 | return -1 21 | try: 22 | readline.parse_and_bind('tab: complete') 23 | except: 24 | return -2 25 | local = os.path.expanduser('~/.local') 26 | if not os.path.exists(local): 27 | try: 28 | os.mkdir(local) 29 | except: 30 | return -2 31 | if not os.path.exists(local + '/var'): 32 | try: 33 | os.mkdir(local + '/var') 34 | except: 35 | return -3 36 | history = local + '/var/python%d_hist'%sys.version_info[0] 37 | try: 38 | readline.read_history_file(history) 39 | except: 40 | pass 41 | atexit.register(readline.write_history_file, history) 42 | return 0 43 | 44 | __completion_init() 45 | del __completion_init 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /script/crond.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skywind3000/collection/add7802471ed96839312b1e917263beb08bed2c4/script/crond.py -------------------------------------------------------------------------------- /script/dosbox.py: -------------------------------------------------------------------------------- 1 | import sys, os, time 2 | 3 | 4 | #---------------------------------------------------------------------- 5 | # DOSBox 6 | #---------------------------------------------------------------------- 7 | class DOSBox (object): 8 | 9 | def __init__ (self, path = '.'): 10 | self.dirhome = '' 11 | work = os.path.dirname(__file__) 12 | for dd in ['.', 'dosbox', path]: 13 | path = os.path.abspath(os.path.join(work, dd)) 14 | if os.path.exists(os.path.join(path, 'dosbox.exe')): 15 | self.dirhome = path 16 | break 17 | if not self.dirhome: 18 | raise IOError('not find dos box') 19 | self.dosbox = self.pathshort(os.path.join(self.dirhome, 'dosbox.exe')) 20 | self.config = {} 21 | self.command = [] 22 | self.default() 23 | 24 | def pathshort (self, path): 25 | if path == None: 26 | return None 27 | path = os.path.abspath(path) 28 | if sys.platform[:3] != 'win': 29 | return path 30 | kernel32 = None 31 | textdata = None 32 | GetShortPathName = None 33 | try: 34 | import ctypes 35 | kernel32 = ctypes.windll.LoadLibrary("kernel32.dll") 36 | textdata = ctypes.create_string_buffer('\000' * 1024) 37 | GetShortPathName = kernel32.GetShortPathNameA 38 | args = [ ctypes.c_char_p, ctypes.c_char_p, ctypes.c_int ] 39 | GetShortPathName.argtypes = args 40 | GetShortPathName.restype = ctypes.c_uint32 41 | except: 42 | pass 43 | if not GetShortPathName: 44 | return path 45 | retval = GetShortPathName(path, textdata, 1024) 46 | shortpath = textdata.value 47 | if retval <= 0: 48 | return '' 49 | return shortpath 50 | 51 | def default (self): 52 | self.config['fullscreen'] = 'false' 53 | self.config['fulldouble'] = 'false' 54 | self.config['fullresolution'] = 'original' 55 | self.config['windowresolution'] = 'original' 56 | self.config['memsize'] = 16 57 | self.config['cycles'] = 15000 58 | self.config['machine'] = 'svga_s3' 59 | 60 | def dump (self): 61 | import StringIO 62 | fp = StringIO.StringIO() 63 | fp.write('[sdl]\n') 64 | for n in ('fullscreen', 'fulldouble', 'fullresolution', 'windowresolution'): 65 | fp.write('%s=%s\n'%(n, self.config[n])) 66 | fp.write('output=opengl\nautolock=true\nsensitivity=100\nwaitonerror=true\n') 67 | fp.write('priority=higher,normal\nmapperfile=mapper-0.74.map\nusescancodes=true\n\n') 68 | fp.write('[dosbox]\n') 69 | fp.write('language=\n') 70 | fp.write('machine=%s\n'%self.config['machine']) 71 | fp.write('captures=capture\nmemsize=%s\n\n'%self.config['memsize']) 72 | fp.write('[render]\n') 73 | fp.write('frameskip=0\naspect=false\nscaler=normal2x\n\n') 74 | fp.write('[cpu]\n') 75 | fp.write('core=auto\ncputype=auto\n') 76 | fp.write('cycles=%s\n'%self.config['cycles']) 77 | fp.write('cycleup=10\ncycledown=20\n\n') 78 | fp.write('[mixer]\n') 79 | fp.write('nosound=false\nrate=44100\nblocksize=1024\nprebuffer=20\n\n') 80 | fp.write('[midi]\n') 81 | fp.write('mpu401=intelligent\nmididevice=default\nmidiconfig=\n\n') 82 | fp.write('[sblaster]\n') 83 | fp.write('sbtype=sb16\nsbbase=220\nirq=7\ndma=1\nhdma=5\nsbmixer=true\n') 84 | fp.write('oplmode=auto\noplemu=default\noplrate=44100\n\n') 85 | fp.write('[gus]\n') 86 | fp.write('gus=false\ngusrate=44100\ngusbase=240\ngusirq=5\ngusdma=3\n') 87 | fp.write('ultradir=C:\ULTRASND\n\n') 88 | fp.write('[speaker]\n') 89 | fp.write('pcspeaker=true\npcrate=44100\ntandy=auto\ntandyrate=44100\n') 90 | fp.write('disney=true\n\n') 91 | fp.write('[joystick]\n') 92 | fp.write('joysticktype=auto\ntimed=true\nautofire=false\nswap34=false\n') 93 | fp.write('buttonwrap=false\n\n') 94 | fp.write('[dos]\n') 95 | fp.write('xms=true\nems=true\numb=true\nkeyboardlayout=auto\n\n') 96 | fp.write('[serial]\n') 97 | fp.write('serial1=dummy\nserial2=dummy\nserial3=disabled\n') 98 | fp.write('serial4=disabled\n\n') 99 | fp.write('[ipx]\nipx=false\n\n\n') 100 | fp.write('[autoexec]\n\n') 101 | for cmd in self.command: 102 | fp.write('%s\n'%cmd) 103 | fp.write('\n') 104 | return fp.getvalue() 105 | 106 | def push (self, text): 107 | self.command.append(text) 108 | 109 | def mount (self, drive, path, opt = ''): 110 | path = self.pathshort(path) 111 | self.push('mount %s %s %s'%(drive, path, opt)) 112 | 113 | def save (self, filename): 114 | text = self.dump() 115 | fp = open(filename, 'w') 116 | fp.write(text) 117 | fp.close() 118 | return text 119 | 120 | def run (self, filename): 121 | import subprocess 122 | filename = self.pathshort(filename) 123 | subprocess.Popen([self.dosbox, '-CONF', filename, '-NOCONSOLE']) 124 | 125 | 126 | #---------------------------------------------------------------------- 127 | # main 128 | #---------------------------------------------------------------------- 129 | def main(args = None): 130 | if args == None: 131 | args = [ n for n in sys.argv ] 132 | args = [ n for n in args ] 133 | import optparse 134 | p = optparse.OptionParser('usage: %prog [options] to start dosbox') 135 | p.add_option('-e', '--exe', dest = 'exe', help = 'executable path') 136 | p.add_option('-f', '--fullscreen', dest = 'fullscreen', action = 'store_true', help = 'full screen') 137 | p.add_option('-c', '--cycles', dest = 'cycles', help = 'cpu cycles, default is 15000') 138 | p.add_option('-s', '--fullsize', dest = 'fullsize', help = 'full-screen resolution') 139 | p.add_option('-w', '--windowsize', dest = 'windowsize', help = 'window size') 140 | p.add_option('-m', '--memsize', dest = 'memsize', help = 'memory size in MB') 141 | p.add_option('-x', '--exit', dest = 'exit', action = 'store_true', help = 'exit after finish') 142 | p.add_option('-d', '--dosbox', dest = 'dosbox', help = 'dos box location') 143 | p.add_option('-p', '--prelaunch', dest = 'prelaunch', help = 'pre-launch bat file') 144 | p.add_option('-t', '--post', dest = 'postlaunch', help = 'post-launch bat file') 145 | p.add_option('-v', '--vga', dest = 'vga', help = 'vga mode: default is svga_s3') 146 | p.add_option('-b', '--verbose', dest = 'verbose', action = 'store_true', help = 'not show cmd') 147 | options, args = p.parse_args(args) 148 | if not options.exe: 149 | print >>sys.stderr, 'No executable file name. Try --help for more information.' 150 | return 2 151 | #print 'exename', options.exe 152 | if not os.path.exists(options.exe): 153 | print >>sys.stderr, 'not find executable file %s, Try --help for more information.'%options.exe 154 | return 3 155 | exename = os.path.abspath(options.exe) 156 | exepath = os.path.dirname(exename) 157 | dosbox = options.dosbox and options.dosbox or '' 158 | DB = DOSBox(dosbox) 159 | exename = DB.pathshort(exename) 160 | exemain = os.path.split(exename)[-1] 161 | cfgname = os.path.splitext(exemain)[0] + '.dos' 162 | 163 | if options.fullscreen: 164 | DB.config['fullscreen'] = 'true' 165 | if options.cycles: 166 | DB.config['cycles'] = options.cycles 167 | if options.fullsize: 168 | DB.config['fullresolution'] = options.fullsize 169 | if options.windowsize: 170 | DB.config['windowresolution'] = options.windowsize 171 | if options.memsize: 172 | DB.config['memsize'] = options.memsize 173 | if options.vga: 174 | DB.config['machine'] = options.vga 175 | if options.verbose: 176 | DB.push('@echo off') 177 | 178 | DB.mount('c', exepath) 179 | 180 | if options.prelaunch: 181 | if os.path.exists(options.prelaunch): 182 | DB.push('REM pre-launch: ' + options.prelaunch) 183 | for line in open(options.prelaunch): 184 | line = line.rstrip('\r\n\t') 185 | if not line: 186 | continue 187 | DB.push(line) 188 | DB.push('') 189 | 190 | DB.push('REM execute: ' + options.exe) 191 | DB.push('C:') 192 | DB.push('CD \\') 193 | DB.push(exemain) 194 | 195 | if options.postlaunch: 196 | if os.path.exists(options.postlaunch): 197 | DB.push('REM post-launch: ' + options.postlaunch) 198 | for line in open(options.postlaunch): 199 | line = line.rstrip('\r\n\t') 200 | if not line: 201 | continue 202 | DB.push(line) 203 | DB.push('') 204 | 205 | if options.exit: 206 | DB.push('EXIT') 207 | 208 | os.chdir(exepath) 209 | print DB.save(cfgname) 210 | DB.run(cfgname) 211 | 212 | return 0 213 | 214 | 215 | #---------------------------------------------------------------------- 216 | # testing case 217 | #---------------------------------------------------------------------- 218 | if __name__ == '__main__': 219 | 220 | def test1(): 221 | db = DOSBox() 222 | db.mount('d', 'd:/games/tt/tt') 223 | db.push('d:') 224 | db.push('tt.exe') 225 | print db.dump() 226 | db.save('d:/temp/tt.conf') 227 | db.run('d:/temp/tt.conf') 228 | 229 | def test2(): 230 | #main(['', '--help']) 231 | main(['', '--exe=d:/games/tt/tt/tt.exe', '-w', '1000x800', '-x', '--dosbox=d:\\games\\tt\\dosbox']) 232 | return 0 233 | 234 | #test2() 235 | main() 236 | 237 | 238 | -------------------------------------------------------------------------------- /script/echosrv.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import time 3 | import socket 4 | import os 5 | 6 | 7 | #---------------------------------------------------------------------- 8 | # udp echo server 9 | #---------------------------------------------------------------------- 10 | def udp_echo_server(ip, port): 11 | sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) 12 | if sys.platform[:3] != 'win': 13 | sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 14 | BUF_SIZE = 8 * 1024 * 1024 15 | sock.setsockopt(socket.SOL_SOCKET, socket.SO_SNDBUF, BUF_SIZE) 16 | sock.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF, BUF_SIZE) 17 | #print sock.getsockopt(socket.SOL_SOCKET, socket.SO_SNDBUF) 18 | #print sock.getsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF) 19 | try: 20 | sock.bind((ip, port)) 21 | except socket.error, e: 22 | sys.stderr.write('bind (%s, %d) failed: %s'%(ip, port, str(e))) 23 | sock.close() 24 | return -1 25 | print 'listening udp on ("%s", %d)'%(ip, port) 26 | while True: 27 | try: 28 | data, remote = sock.recvfrom(8192) 29 | except socket.error, e: 30 | continue 31 | try: 32 | sock.sendto(data, remote) 33 | except socket.error, e: 34 | pass 35 | return 0 36 | 37 | 38 | #---------------------------------------------------------------------- 39 | # entry 40 | #---------------------------------------------------------------------- 41 | def main(args = None): 42 | if args == None: 43 | args = [ n for n in sys.argv ] 44 | import optparse 45 | p = optparse.OptionParser('usage: %prog [options] to start cron') 46 | p.add_option('-p', '--port', dest = 'port', help = 'config port number') 47 | p.add_option('-i', '--ip', dest = 'ip', help = 'config ip address') 48 | options, args = p.parse_args(args) 49 | if options.port is None: 50 | print >>sys.stderr, 'no port given, Try --help for more information.' 51 | return 1 52 | if options.ip is None: 53 | print >>sys.stderr, 'no ip given, Try --help for more information.' 54 | return 1 55 | udp_echo_server(options.ip, int(options.port)) 56 | return 0 57 | 58 | 59 | #---------------------------------------------------------------------- 60 | # testing case 61 | #---------------------------------------------------------------------- 62 | if __name__ == '__main__': 63 | #main(['', '--port=2015', '--ip=0.0.0.0']) 64 | main() 65 | 66 | -------------------------------------------------------------------------------- /script/fiction_mangg.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | #====================================================================== 4 | # 5 | # fiction_mangg.py - fiction download from mangg.com 6 | # 7 | # Created by skywind on 2016/11/29 8 | # Last change: 2016/11/29 17:33:26 9 | # 10 | #====================================================================== 11 | import shell 12 | import re 13 | 14 | 15 | 16 | #---------------------------------------------------------------------- 17 | # FictionMangg 18 | #---------------------------------------------------------------------- 19 | class FictionMangg (object): 20 | 21 | def __init__ (self, url): 22 | self._url = url 23 | if 1: 24 | self._content = shell.request_safe(url).decode('utf-8', 'ignore') 25 | self._content = self._content.encode('gbk', 'ignore') 26 | self._content = self._content.decode('gbk', 'ignore') 27 | else: 28 | self._content = open('mangg.txt', 'r').read().decode('gbk') 29 | content = self._content 30 | p = re.compile(r'
([^<]*)') 31 | result = [[0, x[0], x[1]] for x in p.findall(content)] 32 | for m in result: 33 | m[0] = int(re.findall('/id\d*/(\d*)', m[1])[0]) 34 | result.sort() 35 | self._index = [(m[1] + '.html', m[2]) for m in result] 36 | intro = '' 37 | p1 = content.find('
') 38 | p2 = content.find('
', p1) 39 | if p1 >= 0 and p2 >= 0: 40 | intro = content[p1:p2] 41 | intro = shell.html2text(intro) + '\n\n' 42 | self._intro = intro 43 | 44 | def __len__ (self): 45 | return len(self._index) 46 | 47 | def __getitem__ (self, n): 48 | return self._index[n][1] 49 | 50 | def read_chapter (self, n): 51 | url = 'http://www.mangg.com/' + self._index[n][0] 52 | text = shell.request_safe(url).decode('utf-8', 'ignore') 53 | text = text.encode('gbk', 'ignore').decode('gbk', 'ignore') 54 | return text 55 | 56 | def chapter (self, n): 57 | content = self.read_chapter(n) 58 | p = re.compile(r'
(.*)
') 59 | result = p.findall(content) 60 | html = result[0] 61 | return shell.html2text(html) 62 | 63 | def download (self, filename): 64 | part = [] 65 | size = len(self._index) 66 | for i in xrange(size): 67 | text = self._index[i][1] + '\n\n' 68 | print '[%d/%d] %s'%(i + 1, size, self._index[i][1]) 69 | text+= self.chapter(i) + '\n\n' 70 | part.append(text) 71 | text = '\n'.join(part) 72 | self._whole = self._intro + text 73 | open(filename, 'w').write(self._whole.encode('utf-8')) 74 | print 'saved:', filename 75 | return 0 76 | 77 | 78 | 79 | #---------------------------------------------------------------------- 80 | # simple download interface 81 | #---------------------------------------------------------------------- 82 | def download (url, filename): 83 | mangg = FictionMangg(url) 84 | mangg.download(filename) 85 | return 0 86 | 87 | 88 | #---------------------------------------------------------------------- 89 | # main program 90 | #---------------------------------------------------------------------- 91 | if __name__ == '__main__': 92 | url = 'http://www.mangg.com/id31957/' 93 | 94 | def test1(): 95 | mangg = FictionMangg(url) 96 | for m, n in mangg._index: 97 | print m, n 98 | #print mangg.chapter(0) 99 | return 0 100 | 101 | def test2(): 102 | download(url, 'e:/fiction2.txt') 103 | return 0 104 | 105 | test2() 106 | 107 | 108 | -------------------------------------------------------------------------------- /script/fiction_zhuaji.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | #====================================================================== 4 | # 5 | # fiction_zhuaji.py - zhuaji.org fiction download 6 | # 7 | # Created by skywind on 2016/11/29 8 | # Last change: 2016/11/29 17:33:34 9 | # 10 | #====================================================================== 11 | import shell 12 | import re 13 | 14 | 15 | class FictionZhuaJi (object): 16 | 17 | def __init__ (self, url): 18 | self._url = url 19 | self._content = shell.request_safe(url).decode('gbk', 'ignore') 20 | #self._content = open('content.txt', 'r').read().decode('gbk') 21 | content = self._content 22 | p = re.compile(r'
') 29 | p2 = content.find('', p1) 30 | intro = '' 31 | if p1 >= 0 and p2 >= 0: 32 | text = content[p1:p2] 33 | intro = shell.html2text(text) + '\n\n' 34 | self._intro = intro 35 | 36 | def __len__ (self): 37 | return len(self._index) 38 | 39 | def __getitem__ (self, n): 40 | return self._index[n][1] 41 | 42 | def read_chapter (self, n): 43 | if self._url[-1] == '/': 44 | url = self._url + self._index[n][0] 45 | else: 46 | url = self._url + '/' + self._index[n][0] 47 | return shell.request_safe(url).decode('gbk', 'ignore') 48 | 49 | def chapter (self, n): 50 | content = self.read_chapter(n) 51 | p = re.compile(r'
(.*)
') 52 | result = p.findall(content) 53 | html = result[0] 54 | return shell.html2text(html) 55 | 56 | def download (self, filename): 57 | part = [] 58 | size = len(self._index) 59 | for i in xrange(size): 60 | text = self._index[i][1] + '\n\n' 61 | print '[%d/%d] %s'%(i + 1, size, self._index[i][1]) 62 | text+= self.chapter(i) + '\n\n' 63 | part.append(text) 64 | import time 65 | #time.sleep(2) 66 | text = '\n'.join(part) 67 | self._whole = self._intro + text 68 | open(filename, 'w').write(self._whole.encode('utf-8')) 69 | print 'saved:', filename 70 | return 0 71 | 72 | 73 | 74 | #---------------------------------------------------------------------- 75 | # simple download interface 76 | #---------------------------------------------------------------------- 77 | def download (url, filename): 78 | zj = FictionZhuaJi(url) 79 | zj.download(filename) 80 | return 0 81 | 82 | 83 | #---------------------------------------------------------------------- 84 | # main program 85 | #---------------------------------------------------------------------- 86 | if __name__ == '__main__': 87 | url = 'http://www.zhuaji.org/read/179/' 88 | 89 | def test1(): 90 | zj = FictionZhuaJi(url) 91 | #print zj.chapter(0) 92 | return 0 93 | 94 | def test2(): 95 | download(url, 'e:/fiction.txt') 96 | return 0 97 | 98 | test2() 99 | 100 | 101 | -------------------------------------------------------------------------------- /script/field256.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | #====================================================================== 4 | # 5 | # field256.py - 6 | # 7 | # Created by skywind on 2018/05/14 8 | # Last Modified: 2018/05/14 23:48:17 9 | # 10 | #====================================================================== 11 | from __future__ import print_function 12 | 13 | 14 | #---------------------------------------------------------------------- 15 | # GF(2^8) 16 | #---------------------------------------------------------------------- 17 | class GF256 (object): 18 | 19 | def __init__ (self): 20 | self.table = [0] * 256 21 | self.inverse = [0] * 256 22 | self.arc_table = [0] * 256 23 | self._init_table() 24 | self._init_arc() 25 | self._init_inverse() 26 | 27 | # table[x] = g ^ x 28 | def _init_table (self): 29 | self.table[0] = 1 30 | for i in range(1, 255): 31 | self.table[i] = (self.table[i - 1] << 1) ^ self.table[i - 1] 32 | if (self.table[i] & 0x100) != 0: 33 | self.table[i] ^= 0x11B 34 | return 0 35 | 36 | # arc_table[y] = log(g, y) 37 | def _init_arc (self): 38 | for i in range(255): 39 | self.arc_table[self.table[i]] = i 40 | return 0 41 | 42 | # inverse[x] = 1 / x 43 | def _init_inverse (self): 44 | for i in range(1, 256): 45 | k = self.arc_table[i] 46 | k = (255 - k) % 255 47 | self.inverse[i] = self.table[k] 48 | return 0 49 | 50 | def add (self, x, y): 51 | return x ^ y 52 | 53 | def sub (self, x, y): 54 | return x ^ y 55 | 56 | # x * y = g ^ {[log(g, x) + log(g, y)] % 255} 57 | def mul (self, x, y): 58 | if x == 0 or y == 0: 59 | return 0 60 | return self.table[(self.arc_table[x] + self.arc_table[y]) % 255] 61 | 62 | # x / y = x * (1 / y) 63 | def div (self, x, y): 64 | return self.mul(x, self.inverse[y]) 65 | 66 | def test (self): 67 | for a in range(256): 68 | for b in range(256): 69 | k = self.mul(a, b) 70 | assert (k >= 0 and k < 256) 71 | # print(a, b, k) 72 | if a > 0 and b > 0: 73 | assert self.div(k, a) == b 74 | assert self.div(k, b) == a 75 | print('pass') 76 | return 0 77 | 78 | 79 | #---------------------------------------------------------------------- 80 | # test GF(2^8) 81 | #---------------------------------------------------------------------- 82 | if __name__ == '__main__': 83 | gf = GF256() 84 | gf.test() 85 | 86 | # vim: set ts=4 sw=4 tw=0 noet : 87 | -------------------------------------------------------------------------------- /script/filesync2.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | #====================================================================== 4 | # 5 | # file_time_sync.py - 6 | # 7 | # Created by skywind on 2020/12/08 8 | # Last Modified: 2020/12/08 01:08:36 9 | # 10 | #====================================================================== 11 | from __future__ import print_function, unicode_literals 12 | import sys 13 | import time 14 | import os 15 | import codecs 16 | import shutil 17 | 18 | 19 | #---------------------------------------------------------------------- 20 | # configure 21 | #---------------------------------------------------------------------- 22 | class Configure (object): 23 | 24 | def __init__ (self, ininame = None): 25 | self.dirname = os.path.dirname(os.path.abspath(__file__)) 26 | if ininame is None: 27 | ininame = os.path.split(__file__)[-1] 28 | ininame = os.path.splitext(ininame)[0] + '.ini' 29 | self.ininame = os.path.abspath(os.path.join(self.dirname, ininame)) 30 | self.logname = os.path.splitext(self.ininame)[0] + '.log' 31 | # print(self.ininame) 32 | self.config = self.read_ini(self.ininame) 33 | self.tasks = {} 34 | self.history = os.path.join(self.dirname, 'history') 35 | self.__read_tasks() 36 | self.__read_default() 37 | 38 | def __read_tasks (self): 39 | self.tasks = {} 40 | inihome = os.path.dirname(self.ininame) 41 | for name in self.config: 42 | if name == 'default': 43 | continue 44 | items = self.config[name] 45 | task = [] 46 | index = 1 47 | while True: 48 | key = 'file%d'%index 49 | if key not in items: 50 | break 51 | filename = items[key].strip() 52 | if filename: 53 | if '~' in filename: 54 | filename = os.path.expanduser(filename) 55 | if not os.path.isabs(filename): 56 | filename = os.path.join(inihome, filename) 57 | filename = os.path.abspath(filename) 58 | task.append(filename) 59 | index += 1 60 | if task: 61 | self.tasks[name] = task 62 | return True 63 | 64 | def __read_default (self): 65 | if 'default' not in self.config: 66 | return False 67 | items = self.config['default'] 68 | inihome = os.path.dirname(self.ininame) 69 | if 'history' in items: 70 | history = items['history'] 71 | if '~' in history: 72 | history = os.path.expanduser(history) 73 | history = os.path.join(inihome, history) 74 | self.history = os.path.abspath(history) 75 | if 'log' in items: 76 | log = items['log'].strip() 77 | if not log: 78 | self.logname = None 79 | else: 80 | if '~' in log: 81 | log = os.path.expanduser(log) 82 | self.logname = os.path.abspath(os.path.join(inihome, log)) 83 | return True 84 | 85 | def get_mtime (self, path): 86 | if not os.path.isfile(path): 87 | return -1 88 | return int(os.path.getmtime(path) * 1000) 89 | 90 | def copy_file (self, src, dst): 91 | src = os.path.abspath(src) 92 | dst = os.path.abspath(dst) 93 | if src == dst: 94 | return False 95 | path = os.path.dirname(dst) 96 | if not os.path.isdir(path): 97 | try: 98 | os.makedirs(path) 99 | except OSError as e: 100 | print('ERROR: create directory failed:') 101 | print(e) 102 | return False 103 | try: 104 | shutil.copyfile(src, dst) 105 | shutil.copystat(src, dst) 106 | except OSError as e: 107 | print('ERROR: copy file failed:') 108 | print(e) 109 | return False 110 | return True 111 | 112 | def need_update (self, task_name): 113 | if task_name not in self.tasks: 114 | return -1 115 | task = self.tasks[task_name] 116 | size = len(task) 117 | if size <= 0: 118 | return -1 119 | checks = [] 120 | first_check = None 121 | for name in task: 122 | check = self.get_mtime(name) 123 | if not checks: 124 | first_check = check 125 | checks.append(check) 126 | equals = True 127 | for index, name in enumerate(task): 128 | if first_check != checks[index]: 129 | equals = False 130 | break 131 | if equals: 132 | return -1 133 | max_pos = 0 134 | max_val = checks[0] 135 | for i in range(size): 136 | if checks[i] > max_val: 137 | max_pos = i 138 | max_val = checks[i] 139 | return max_pos 140 | 141 | def read_ini (self, name, encoding = None): 142 | obj = self.load_ini(name, encoding) 143 | if not obj: 144 | obj = {} 145 | else: 146 | newobj = {} 147 | for sect in obj: 148 | section = {} 149 | for k, v in obj[sect].items(): 150 | section[k.lower()] = v 151 | newobj[sect.lower()] = section 152 | obj = newobj 153 | return obj 154 | 155 | # load ini without ConfigParser 156 | def load_ini (self, filename, encoding = None): 157 | text = self.load_file_text(filename, encoding) 158 | config = {} 159 | sect = 'default' 160 | if text is None: 161 | return None 162 | for line in text.split('\n'): 163 | line = line.strip('\r\n\t ') 164 | if not line: 165 | continue 166 | elif line[:1] in ('#', ';'): 167 | continue 168 | elif line.startswith('['): 169 | if line.endswith(']'): 170 | sect = line[1:-1].strip('\r\n\t ') 171 | if sect not in config: 172 | config[sect] = {} 173 | else: 174 | pos = line.find('=') 175 | if pos >= 0: 176 | key = line[:pos].rstrip('\r\n\t ') 177 | val = line[pos + 1:].lstrip('\r\n\t ') 178 | if sect not in config: 179 | config[sect] = {} 180 | config[sect][key] = val 181 | return config 182 | 183 | # load file and guess encoding 184 | def load_file_text (self, filename, encoding = None): 185 | content = self.load_file_content(filename, 'rb') 186 | if content is None: 187 | return None 188 | if content[:3] == b'\xef\xbb\xbf': 189 | text = content[3:].decode('utf-8') 190 | elif encoding is not None: 191 | text = content.decode(encoding, 'ignore') 192 | else: 193 | text = None 194 | guess = [sys.getdefaultencoding(), 'utf-8'] 195 | if sys.stdout and sys.stdout.encoding: 196 | guess.append(sys.stdout.encoding) 197 | try: 198 | import locale 199 | guess.append(locale.getpreferredencoding()) 200 | except: 201 | pass 202 | visit = {} 203 | for name in guess + ['gbk', 'ascii', 'latin1']: 204 | if name in visit: 205 | continue 206 | visit[name] = 1 207 | try: 208 | text = content.decode(name) 209 | break 210 | except: 211 | pass 212 | if text is None: 213 | text = content.decode('utf-8', 'ignore') 214 | return text 215 | 216 | # load content 217 | def load_file_content (self, filename, mode = 'r'): 218 | if hasattr(filename, 'read'): 219 | try: content = filename.read() 220 | except: pass 221 | return content 222 | try: 223 | fp = open(filename, mode) 224 | content = fp.read() 225 | fp.close() 226 | except: 227 | content = None 228 | return content 229 | 230 | def sp_decode (self, text): 231 | code = b'' 232 | size = len(text) 233 | index = 0 234 | text = text.replace('##', '--') 235 | while index < size: 236 | ch = text[index] 237 | if index + 2 < size: 238 | nc = text[index + 1] 239 | if nc != '#': 240 | code += bytes([ord(ch)]) 241 | index += 1 242 | else: 243 | nc = text[index + 2] 244 | if nc == '#': 245 | code += bytes([ord(ch), ord('#'), ord('#')]) 246 | index += 3 247 | else: 248 | code += bytes([int(ch + nc, 16)]) 249 | index += 3 250 | else: 251 | code += bytes([ord(ch)]) 252 | index += 1 253 | return code.decode('utf-8', 'ignore') 254 | 255 | 256 | #---------------------------------------------------------------------- 257 | # FileSync 258 | #---------------------------------------------------------------------- 259 | class FileSync (object): 260 | 261 | def __init__ (self, ininame = None): 262 | self.config = Configure(ininame) 263 | self.__log_fp = None 264 | 265 | # write log 266 | def log (self, *args): 267 | text = ' '.join([ str(n) for n in args ]) 268 | if not self.__log_fp: 269 | fp = codecs.open(self.config.logname, 'a', encoding = 'utf-8') 270 | self.__log_fp = fp 271 | now = time.strftime('%Y-%m-%d %H:%M:%S') 272 | line = '[%s] %s'%(now, text) 273 | self.__log_fp.write(line + '\n') 274 | self.__log_fp.flush() 275 | print(line) 276 | return 0 277 | 278 | def task_list (self): 279 | tasks = self.config.tasks 280 | for name in tasks: 281 | print('%s:'%name) 282 | task = tasks[name] 283 | check = self.config.need_update(name) 284 | for index, fn in enumerate(task): 285 | if index != check: 286 | print(' ' + fn) 287 | else: 288 | print(' * ' + fn) 289 | print('') 290 | return 0 291 | 292 | def task_update (self, name): 293 | task = self.config.tasks.get(name) 294 | if not task: 295 | print('error: file group %s does not exist !'%name) 296 | return False 297 | check = self.config.need_update(name) 298 | if check < 0: 299 | print('file group %s is clear.'%name) 300 | return False 301 | source = task[check] 302 | self.log('[update] file group dirty: %s'%(name)) 303 | self.log('source: ' + source) 304 | for index, fn in enumerate(task): 305 | if index != check: 306 | self.config.copy_file(source, task[index]) 307 | self.log('sync -> %s'%(task[index], )) 308 | if not os.path.exists(self.config.history): 309 | os.makedirs(self.config.history) 310 | mtime = self.config.get_mtime(source) 311 | mtext = time.strftime('%Y%m%d_%H%M%S', time.localtime(mtime * 0.001)) 312 | hname = name + '.' + mtext 313 | history = os.path.join(self.config.history, hname) 314 | self.log('record: %s'%hname) 315 | self.config.copy_file(source, history) 316 | return True 317 | 318 | def task_sync (self): 319 | for name in self.config.tasks: 320 | if self.config.need_update(name) >= 0: 321 | self.task_update(name) 322 | return True 323 | 324 | def sol_dump (self): 325 | sols = [] 326 | appdata = os.environ.get('APPDATA') 327 | if not appdata: 328 | return None 329 | prefix = 'Macromedia/Flash Player/#SharedObjects' 330 | solhome = os.path.abspath(os.path.join(appdata, prefix)) 331 | size = len(solhome) 332 | for root, dirs, files in os.walk(solhome): 333 | for fn in files: 334 | if os.path.splitext(fn)[-1].lower() == '.sol': 335 | path = os.path.abspath(os.path.join(root, fn)) 336 | short = path[size+1:] 337 | sols.append((path, short)) 338 | return sols 339 | 340 | def sol_local (self): 341 | sols = [] 342 | for path, short in self.sol_dump(): 343 | test = short.replace('\\', '/') 344 | p1 = test.find('/') 345 | if p1 < 0: 346 | continue 347 | p2 = test.find('/', p1 + 1) 348 | if p2 < 0: 349 | continue 350 | name = short[p1 + 1:p2] 351 | if name != 'localhost': 352 | if name != '#localWithNet': 353 | continue 354 | mark = short[p2 + 1:] 355 | # print(mark) 356 | mark = self.config.sp_decode(mark) 357 | sols.append((path, mark)) 358 | return sols 359 | 360 | def sol_list (self, limit = None): 361 | sols = self.sol_local() 362 | items = [] 363 | for path, short in sols: 364 | # print(short) 365 | mtime = self.config.get_mtime(path) 366 | items.append((mtime, path, short)) 367 | items.sort(reverse = True) 368 | if limit is not None: 369 | items = items[:limit] 370 | for mtime, path, short in items: 371 | ts = time.localtime(mtime * 0.001) 372 | ts = time.strftime('%Y-%m-%d %H:%M:%S', ts) 373 | try: 374 | print('%s'%(path,)) 375 | print('(%s): %s'%(ts, short)) 376 | print() 377 | except: 378 | pass 379 | return True 380 | 381 | 382 | #---------------------------------------------------------------------- 383 | # getopt: returns (options, args) 384 | #---------------------------------------------------------------------- 385 | def getopt (argv): 386 | args = [] 387 | options = {} 388 | if argv is None: 389 | argv = sys.argv[1:] 390 | index = 0 391 | count = len(argv) 392 | while index < count: 393 | arg = argv[index] 394 | if arg != '': 395 | head = arg[:1] 396 | if head != '-': 397 | break 398 | if arg == '-': 399 | break 400 | name = arg.lstrip('-') 401 | key, _, val = name.partition('=') 402 | options[key.strip()] = val.strip() 403 | index += 1 404 | while index < count: 405 | args.append(argv[index]) 406 | index += 1 407 | return options, args 408 | 409 | 410 | #---------------------------------------------------------------------- 411 | # main 412 | #---------------------------------------------------------------------- 413 | def main(args = None): 414 | args = args and args or sys.argv 415 | args = [ n for n in args ] 416 | options, args = getopt(args[1:]) 417 | if not options: 418 | prog = os.path.split(__file__)[-1] 419 | print('usage: %s [ininame]'%prog) 420 | print('available operations:') 421 | print(' %s {-l --list} [ininame]'%prog) 422 | print(' %s {-s --sync} [ininame]'%prog) 423 | print() 424 | return 0 425 | if ('f' in options) or ('flash' in options): 426 | fs = FileSync() 427 | fs.sol_list() 428 | return 0 429 | ininame = None 430 | if len(args) > 0: 431 | ininame = args[0] 432 | if not os.path.exists(ininame): 433 | print('error: "%s" not exists'%ininame) 434 | return 1 435 | ininame = os.path.abspath(ininame) 436 | if not ininame: 437 | test = os.path.expanduser('~/.config/filesync2.ini') 438 | test = os.path.abspath(test) 439 | if os.path.exists(test): 440 | ininame = test 441 | else: 442 | print('error: default ini "%s" missing'%test) 443 | return 2 444 | if ('l' in options) or ('list' in options): 445 | fs = FileSync(ininame) 446 | fs.task_list() 447 | return 0 448 | elif ('s' in options) or ('sync' in options): 449 | fs = FileSync(ininame) 450 | fs.task_sync() 451 | return 0 452 | elif not options: 453 | print('error: empty operation') 454 | return 3 455 | else: 456 | print('error: unknow operation %s'%options) 457 | return 4 458 | return 0 459 | 460 | 461 | #---------------------------------------------------------------------- 462 | # testing suit 463 | #---------------------------------------------------------------------- 464 | if __name__ == '__main__': 465 | 466 | def test1(): 467 | cfg = Configure() 468 | print(cfg.tasks) 469 | print(cfg.get_mtime(__file__)) 470 | print(cfg.logname) 471 | # solsync.py 472 | # flashsol.py 473 | return 0 474 | 475 | def test2(): 476 | ts = FileSync() 477 | ts.task_list() 478 | 479 | def test3(): 480 | ts = FileSync() 481 | ts.config.copy_file('d:/file1.txt', 'd:/file4.txt') 482 | print(ts.config.get_mtime('d:/file1.txt')) 483 | print(ts.config.get_mtime('d:/file4.txt')) 484 | 485 | def test4(): 486 | ts = FileSync() 487 | ts.task_sync() 488 | return 0 489 | 490 | def test5(): 491 | args = ['', '-l', 'filesync2.ini'] 492 | args = ['', '-s', 'filesync2.ini'] 493 | args = ['', '-f'] 494 | main(args) 495 | return 0 496 | 497 | # test5() 498 | main() 499 | 500 | 501 | -------------------------------------------------------------------------------- /script/gamelevel.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/python 2 | 3 | import sys, time, random, math 4 | import cavemake 5 | import sys, random 6 | 7 | from cavemake import CFLOOR, CWALL, CPERMWALL 8 | 9 | 10 | #---------------------------------------------------------------------- 11 | # math 12 | #---------------------------------------------------------------------- 13 | def create_matrix(w = 8, h = 8, c = 0): 14 | m = [ [ c for j in xrange(w) ] for i in xrange(h) ] 15 | return m 16 | 17 | def copy_matrix(m): 18 | return [ [ n for n in line ] for line in m ] 19 | 20 | def matrix_size(m): 21 | if len(m) == 0: return 0, 0 22 | return len(m[0]), len(m) 23 | 24 | def print_matrix(m, is_all_char = False): 25 | mlen = 0 26 | for line in m: 27 | for n in line: 28 | s = len(str(n)) 29 | mlen = max(mlen, s) 30 | for line in m: 31 | text = '' 32 | for n in line: 33 | result = str(n).rjust(mlen) + ' ' 34 | if is_all_char: result = str(n)[:1] 35 | text += result 36 | print text 37 | return 0 38 | 39 | def drawchar(screen, x, y, c): 40 | w, h = matrix_size(screen) 41 | if (x >= 0) and (y >= 0) and (x < w) and (y < h): 42 | screen[y][x] = c 43 | 44 | def drawtext(screen, x, y, text): 45 | for n in text: 46 | drawchar(screen, x, y, n) 47 | x += 1 48 | 49 | def displayscr(screen): 50 | for line in screen: 51 | row = '' 52 | for c in line: row += c 53 | print row 54 | 55 | def drawline(screen, x1, y1, x2, y2, c = '*'): 56 | if y1 == y2: 57 | if x1 > x2: x1, x2 = x2, x1 58 | while x1 <= x2: 59 | drawchar(screen, x1, y1, c) 60 | x1 += 1 61 | return 62 | if x1 == x2: 63 | if y1 > y2: y1, y2 = y2, y1 64 | while y1 <= y2: 65 | drawchar(screen, x1, y1, c) 66 | y1 += 1 67 | return 68 | if abs(x1 - x2) >= abs(y1 - y2): 69 | step = abs(x2 - x1) + 1 70 | incx = 1 71 | if x1 > x2: incx = -1 72 | incy = float(y2 - y1) / step 73 | x = float(x1) 74 | y = float(y1) 75 | for i in xrange(step): 76 | drawchar(screen, int(x), int(y), c) 77 | x += incx 78 | y += incy 79 | else: 80 | step = abs(y2 - y1) + 1 81 | incy = 1 82 | if y1 > y2: incy = -1 83 | incx = float(x2 - x1) / step 84 | x = float(x1) 85 | y = float(y1) 86 | for i in xrange(step): 87 | drawchar(screen, int(x), int(y), c) 88 | x += incx 89 | y += incy 90 | 91 | 92 | #---------------------------------------------------------------------- 93 | # disjointset 94 | #---------------------------------------------------------------------- 95 | class disjointset: 96 | def __init__(self): 97 | self.__father = {} 98 | self.__weight = {} 99 | self.__size = 0 100 | def __len__(self): 101 | return self.__size 102 | def find(self, x): 103 | if x == None: 104 | return None 105 | father = self.__father 106 | if x in father: 107 | root = x 108 | path = [] 109 | while father[root] != None: 110 | path.append(root) 111 | root = father[root] 112 | for n in path: 113 | self.__father[n] = root 114 | return root 115 | self.__father[x] = None 116 | self.__weight[x] = 1 117 | self.__size += 1 118 | return x 119 | def __getitem__(self, key): 120 | return self.find(key) 121 | def weight(self, x): 122 | return self.__weight[self.find(x)] 123 | def clear(self): 124 | self.__father = {} 125 | self.__weight = {} 126 | self.__size = 0 127 | def union(self, x, y): 128 | root1 = self.find(x) 129 | root2 = self.find(y) 130 | if root1 != root2: 131 | if self.__weight[root1] < self.__weight[root2]: 132 | self.__weight[root2] += self.__weight[root1] 133 | del self.__weight[root1] 134 | self.__father[root1] = root2 135 | else: 136 | self.__weight[root1] += self.__weight[root2] 137 | del self.__weight[root2] 138 | self.__father[root2] = root1 139 | def split(self): 140 | roots = {} 141 | for n in self.__father: 142 | f = self.find(n) 143 | if f in roots: 144 | roots[f].append(n) 145 | else: 146 | roots[f] = [n] 147 | return roots 148 | 149 | 150 | class simplebunch: 151 | def __init__ (self, **kwds): self.__dict__ = kwds 152 | 153 | 154 | class vector2d: 155 | def __init__ (self, x = 0, y = 0): 156 | self.x = x 157 | self.y = y 158 | def __add__ (self, p): 159 | if type(p) == type((0, 0)): 160 | return vector2d(self.x + p[0], self.y + p[1]) 161 | return vector2d(self.x + p.x, self.y + p.y) 162 | def __sub__ (self, p): 163 | if type(p) == type((0, 0)): 164 | return vector2d(self.x - p[0], self.y - p[1]) 165 | return vector2d(self.x - p.x, self.y - p.y) 166 | def __radd__ (self, p): 167 | return self.__add__(p) 168 | def __rsub__ (self, p): 169 | if type(p) == type((0, 0)): 170 | return vector2d(p[0] - self.x, p[1] - self.y) 171 | return vector2d(p.x - self.x, p.y - self.y) 172 | def __neg__ (self): 173 | return vector2d(-self.x, -self.y) 174 | def __repr__ (self): 175 | return 'vector2d(%s,%s)'%(self.x, self.y) 176 | def __getitem__ (self, key): 177 | if key == 0: return self.x 178 | if key == 1: return self.y 179 | raise KeyError('unknow key(%s) for vector2d'%key) 180 | def __setitem__ (self, key, val): 181 | if key == 0: self.x = val 182 | elif key == 1: self.y = val 183 | else: raise KeyError('unknow key(%s) for vector2d'%key) 184 | def __eq__ (self, p): 185 | if (self.x == p.x) and (self.y == p.y): 186 | return True 187 | return False 188 | def __ne__ (self, p): 189 | return not self.__eq__(p) 190 | def length (self): 191 | import math 192 | return math.sqrt(self.x * self.x + self.y * self.y) 193 | def dot (self, p): 194 | if type(p) == type((0, 0)): 195 | return self.x * p[0] + self.y * p[1] 196 | return self.x * p.x + self.y * p.y 197 | def __mul__ (self, p): 198 | if type(p) == type((0, 0)): 199 | return self.x * p[1] - self.y * p[0] 200 | elif type(p) in (int, long, float): 201 | return vector2d(self.x * p, self.y * p) 202 | return self.x * p.y - self.y * p.x 203 | def __rmul__ (self, p): 204 | if type(p) == type((0, 0)): 205 | return p[0] * self.y - p[1] * self.x 206 | elif type(p) in (int, long, float): 207 | return vector2d(self.x * p, self.y * p) 208 | return p.x * self.y - p.y * self.x 209 | def __copy__ (self): 210 | return vector2d(self.x, self.y) 211 | def __deepcopy__ (self): 212 | return self.__copy__() 213 | 214 | 215 | class line2d: 216 | def __init__ (self, p1 = (0, 0), p2 = (0, 0)): 217 | self.p1 = vector2d() 218 | self.p2 = vector2d() 219 | if type(p1) == type((0, 0)): 220 | self.p1.x, self.p1.y = p1[0], p1[1] 221 | else: 222 | self.p1.x, self.p1.y = p1.x, p1.y 223 | if type(p2) == type((0, 0)): 224 | self.p2.x, self.p2.y = p2[0], p2[1] 225 | else: 226 | self.p2.x, self.p2.y = p2.x, p2.y 227 | def length (self): 228 | p = self.p2 - self.p1 229 | return p.length() 230 | def __repr__ (self): 231 | p1, p2 = self.p1, self.p2 232 | return 'line(%s,%s,%s,%s)'%(p1.x, p1.y, p2.x, p2.y) 233 | def intersect (self, l): 234 | v1 = (self.p2.x - self.p1.x) * (l.p2.y - self.p1.y) 235 | v2 = (self.p2.x - self.p1.x) * (l.p1.y - self.p1.y) 236 | v1-= (self.p2.y - self.p1.y) * (l.p2.x - self.p1.x) 237 | v2-= (self.p2.y - self.p1.y) * (l.p1.x - self.p1.x) 238 | if (v1 * v2) >= 0: 239 | return False 240 | v3 = (l.p2.x - l.p1.x) * (self.p2.y - l.p1.y) 241 | v4 = (l.p2.x - l.p1.x) * (self.p1.y - l.p1.y) 242 | v3-= (l.p2.y - l.p1.y) * (self.p2.x - l.p1.x) 243 | v4-= (l.p2.y - l.p1.y) * (self.p1.x - l.p1.x) 244 | if (v3 * v4) >= 0: 245 | return False 246 | return True 247 | def online (self, p): 248 | if type(p) == type((0, 0)): 249 | p = vector2d(p[0], p[1]) 250 | if (p - self.p1) * (self.p2 - self.p1) == 0: 251 | return True 252 | return False 253 | def onsegment (self, p): 254 | if type(p) == type((0, 0)): 255 | p = vector2d(p[0], p[1]) 256 | if not self.online(p): 257 | return False 258 | if p.x < min(self.p1.x, self.p2.x): 259 | return False 260 | if p.y < min(self.p1.y, self.p2.y): 261 | return False 262 | if p.x > max(self.p1.x, self.p2.x): 263 | return False 264 | if p.y > max(self.p1.y, self.p2.y): 265 | return False 266 | return True 267 | 268 | 269 | def savebmp(fname, screen): 270 | from struct import pack, unpack 271 | w, h = len(screen[0]), len(screen) 272 | depth = 24 273 | filler = 3 - ((w * h * int(depth / 8) - 1) & 3) 274 | imgsize = (w * 3 + filler) * h 275 | filesize = 54 + imgsize 276 | fp = file(fname, 'wb') 277 | fp.write(pack('> 8) & 0xff), ((c >> 16) & 0xff) 284 | fp.write(pack('= 1: 447 | self.textroom(rooms[0][2], "BOSS") 448 | self.items.append((rooms[0][1], "BOSS")) 449 | IN = self.__entrances[0] 450 | drawtext(self.__display, IN[1] - 2, IN[0] + 1, " IN ") 451 | from random import randint 452 | 453 | for i in xrange(len(rooms)): 454 | pos = randint(0, len(rooms)) + 1 455 | if (pos < 1) or (pos >= len(rooms)): continue 456 | root = rooms[pos][2] 457 | mode = randint(0, 99) 458 | if mode < 40: 459 | self.textroom(root, " X ") 460 | self.items.append((rooms[pos][1], "X")) 461 | elif mode < 60: 462 | self.textroom(root, " X$ ") 463 | self.items.append((rooms[pos][1], "X$")) 464 | elif mode < 80: 465 | self.textroom(root, " $ ") 466 | self.items.append((rooms[pos][1], "$")) 467 | 468 | for root in self.roomset: 469 | room = self.roomset[root] 470 | pt = room.center 471 | drawtext(self.__display, pt[1], pt[0], "C") 472 | 473 | 474 | def display (self): 475 | for line in self.__display: 476 | row = '' 477 | for c in line: 478 | row += c 479 | print row 480 | print 481 | 482 | 483 | 484 | if __name__ == '__main__': 485 | WIDTH = 32 486 | HEIGHT = 32 487 | RATIO = 0.24 488 | if len(sys.argv) > 1: WIDTH = int(sys.argv[1]) 489 | if len(sys.argv) > 2: HEIGHT = int(sys.argv[2]) 490 | if len(sys.argv) > 3: RATIO = int(sys.argv[3]) * 0.01 491 | pop = population(WIDTH, HEIGHT, RATIO) 492 | pop.seed() 493 | pop.generate() 494 | pop.display() 495 | -------------------------------------------------------------------------------- /script/gobang.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skywind3000/collection/add7802471ed96839312b1e917263beb08bed2c4/script/gobang.py -------------------------------------------------------------------------------- /script/googauth.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | #====================================================================== 4 | # 5 | # googauth.py - Google Authenticator 6 | # 7 | # Created by skywind on 2018/08/10 8 | # Last Modified: 2018/08/10 21:50 9 | # 10 | #====================================================================== 11 | from __future__ import print_function 12 | import sys 13 | import time 14 | import base64 15 | import hashlib 16 | import hmac 17 | import struct 18 | import os 19 | 20 | 21 | #---------------------------------------------------------------------- 22 | # python 2/3 compatible 23 | #---------------------------------------------------------------------- 24 | if sys.version_info[0] >= 3: 25 | long = int 26 | unicode = str 27 | xrange = range 28 | 29 | 30 | #---------------------------------------------------------------------- 31 | # generate verification code from secret key and value 32 | #---------------------------------------------------------------------- 33 | def generate_code(secret, value = None): 34 | if value is None: 35 | value = int(time.time() / 30) 36 | value = struct.pack('>q', value) 37 | 38 | token = secret.replace(' ', '').upper() 39 | secretkey = base64.b32decode(token) 40 | 41 | hash = hmac.new(secretkey, value, hashlib.sha1).digest() 42 | 43 | offset = struct.unpack('>B', hash[-1:])[0] & 0xf 44 | truncated_hash = hash[offset:offset + 4] 45 | 46 | truncated_hash = struct.unpack('>L', truncated_hash)[0] 47 | truncated_hash &= 0x7fffffff 48 | truncated_hash %= 1000000 49 | 50 | return '%06d' % truncated_hash 51 | 52 | 53 | #---------------------------------------------------------------------- 54 | # counter based code varification 55 | #---------------------------------------------------------------------- 56 | def verify_counter_based(secret, code, counter, window = 3): 57 | if (not isinstance(code, str)) and (not isinstance(code, unicode)): 58 | raise TypeError('code must be a string') 59 | 60 | for offset in xrange(1, window + 1): 61 | valid_code = generate_code(secret, counter + offset) 62 | if code == valid_code: 63 | return counter + offset 64 | 65 | return None 66 | 67 | 68 | #---------------------------------------------------------------------- 69 | # time based code verification 70 | #---------------------------------------------------------------------- 71 | def verify_time_based(secret, code, window = 3): 72 | if (not isinstance(code, str)) and (not isinstance(code, unicode)): 73 | raise TypeError('code must be a string') 74 | 75 | epoch = int(time.time() / 30) 76 | 77 | for offset in xrange(-(window // 2), window - (window // 2)): 78 | valid_code = generate_code(secret, epoch + offset) 79 | if code == valid_code: 80 | return epoch + offset 81 | 82 | return None 83 | 84 | 85 | #---------------------------------------------------------------------- 86 | # generate_secretkey 87 | #---------------------------------------------------------------------- 88 | def generate_secret_key(length = 16): 89 | 90 | def _generate_random_bytes(): 91 | sha_hash = hashlib.sha512() 92 | sha_hash.update(os.urandom(8192)) 93 | byte_hash = sha_hash.digest() 94 | 95 | for i in xrange(6): 96 | sha_hash = hashlib.sha512() 97 | sha_hash.update(byte_hash) 98 | byte_hash = sha_hash.digest() 99 | 100 | return byte_hash 101 | 102 | if length < 8 or length > 128: 103 | raise TypeError('Secret key length is invalid.') 104 | 105 | byte_hash = _generate_random_bytes() 106 | if length > 102: 107 | byte_hash += _generate_random_bytes() 108 | 109 | text = base64.b32encode(byte_hash)[:length] 110 | text = str(text.decode('latin1')) 111 | 112 | return text 113 | 114 | 115 | #---------------------------------------------------------------------- 116 | # get url 117 | #---------------------------------------------------------------------- 118 | def get_otpauth_url(user, domain, secret): 119 | return 'otpauth://totp/' + user + '@' + domain + '?secret=' + secret 120 | 121 | 122 | #---------------------------------------------------------------------- 123 | # barcode 124 | #---------------------------------------------------------------------- 125 | def get_barcode_url(user, domain, secret): 126 | import urllib 127 | if sys.version_info[0] < 3: 128 | from urllib import urlencode 129 | else: 130 | from urllib.parse import urlencode 131 | url = 'https://www.google.com/chart?chs=200x200&chld=M|0&cht=qr&' 132 | opt_url = get_otpauth_url(user, domain, secret) 133 | url += urlencode({'chl': opt_url}) 134 | return url 135 | 136 | 137 | #---------------------------------------------------------------------- 138 | # tabulify: style = 0, 1, 2 139 | #---------------------------------------------------------------------- 140 | def tabulify (rows, style = 0): 141 | colsize = {} 142 | maxcol = 0 143 | output = [] 144 | if not rows: 145 | return '' 146 | for row in rows: 147 | maxcol = max(len(row), maxcol) 148 | for col, text in enumerate(row): 149 | text = str(text) 150 | size = len(text) 151 | if col not in colsize: 152 | colsize[col] = size 153 | else: 154 | colsize[col] = max(size, colsize[col]) 155 | if maxcol <= 0: 156 | return '' 157 | def gettext(row, col): 158 | csize = colsize[col] 159 | if row >= len(rows): 160 | return ' ' * (csize + 2) 161 | row = rows[row] 162 | if col >= len(row): 163 | return ' ' * (csize + 2) 164 | text = str(row[col]) 165 | padding = 2 + csize - len(text) 166 | pad1 = 1 167 | pad2 = padding - pad1 168 | return (' ' * pad1) + text + (' ' * pad2) 169 | if style == 0: 170 | for y, row in enumerate(rows): 171 | line = ''.join([ gettext(y, x) for x in xrange(maxcol) ]) 172 | output.append(line) 173 | elif style == 1: 174 | if rows: 175 | newrows = rows[:1] 176 | head = [ '-' * colsize[i] for i in xrange(maxcol) ] 177 | newrows.append(head) 178 | newrows.extend(rows[1:]) 179 | rows = newrows 180 | for y, row in enumerate(rows): 181 | line = ''.join([ gettext(y, x) for x in xrange(maxcol) ]) 182 | output.append(line) 183 | elif style == 2: 184 | sep = '+'.join([ '-' * (colsize[x] + 2) for x in xrange(maxcol) ]) 185 | sep = '+' + sep + '+' 186 | for y, row in enumerate(rows): 187 | output.append(sep) 188 | line = '|'.join([ gettext(y, x) for x in xrange(maxcol) ]) 189 | output.append('|' + line + '|') 190 | output.append(sep) 191 | return '\n'.join(output) 192 | 193 | 194 | #---------------------------------------------------------------------- 195 | # load ini 196 | #---------------------------------------------------------------------- 197 | def load_ini(ininame, codec = None): 198 | try: 199 | content = open(ininame, 'rb').read() 200 | except IOError: 201 | content = b'' 202 | if content[:3] == b'\xef\xbb\xbf': 203 | text = content[3:].decode('utf-8') 204 | elif codec is not None: 205 | text = content.decode(codec, 'ignore') 206 | else: 207 | codec = sys.getdefaultencoding() 208 | text = None 209 | for name in [codec, 'gbk', 'utf-8']: 210 | try: 211 | text = content.decode(name) 212 | break 213 | except: 214 | pass 215 | if text is None: 216 | text = content.decode('utf-8', 'ignore') 217 | if sys.version_info[0] < 3: 218 | import StringIO 219 | import ConfigParser 220 | sio = StringIO.StringIO(text) 221 | cp = ConfigParser.ConfigParser() 222 | cp.readfp(sio) 223 | else: 224 | import configparser 225 | cp = configparser.ConfigParser() 226 | cp.read_string(text) 227 | config = {} 228 | for sect in cp.sections(): 229 | for key, val in cp.items(sect): 230 | lowsect, lowkey = sect.lower(), key.lower() 231 | config.setdefault(lowsect, {})[lowkey] = val 232 | return config 233 | 234 | 235 | #---------------------------------------------------------------------- 236 | # list code 237 | #---------------------------------------------------------------------- 238 | def list_code(table, cont): 239 | while True: 240 | current = int(time.time()) 241 | epoch = current // 30 242 | life = 30 - current % 30 243 | rows = [] 244 | rows.append([ 'User', 'Domain', 'Code', 'Life Time' ]) 245 | for record in table: 246 | secret = record[0] 247 | user = record[1] 248 | domain = record[2] 249 | code = generate_code(secret, epoch) 250 | rows.append([ user, domain, code, ' %d (s)'%life ]) 251 | style = os.environ.get('GOOGAUTH_STYLE', 2) 252 | print(tabulify(rows, int(style))) 253 | # print() 254 | if not cont: 255 | break 256 | print('press Ctrl+C to break ...') 257 | try: 258 | time.sleep(1) 259 | except KeyboardInterrupt: 260 | print() 261 | break 262 | print() 263 | return 0 264 | 265 | 266 | #---------------------------------------------------------------------- 267 | # main 268 | #---------------------------------------------------------------------- 269 | def main(argv = None): 270 | argv = argv and argv or sys.argv 271 | argv = [ n for n in argv ] 272 | if len(argv) <= 1: 273 | prog = argv[0] 274 | print('usage: %s [...]'%prog) 275 | print('operations:') 276 | head = ' %s '%prog 277 | print(head, '{-c --create} [user] [domain]') 278 | print(head, '{-v --verify} secret code') 279 | print(head, '{-d --display} secret') 280 | print(head, '{-l --list} filename [--continue]') 281 | return 0 282 | cmd = argv[1] 283 | if cmd in ('-c', '--create'): 284 | key = generate_secret_key() 285 | print('secret:', key) 286 | user = len(argv) > 2 and argv[2] or '' 287 | domain = len(argv) > 3 and argv[3] or '' 288 | print('url:', get_otpauth_url(user, domain, key)) 289 | print('barcode:', get_barcode_url(user, domain, key)) 290 | return 0 291 | if cmd in ('-v', '--verify'): 292 | if len(argv) < 4: 293 | print('require secret and code parameters') 294 | return 1 295 | key = argv[2] 296 | code = argv[3] 297 | if not verify_time_based(key, code): 298 | print('verification failed') 299 | return 2 300 | print('verification succeeded') 301 | return 0 302 | if cmd in ('-d', '--display'): 303 | if len(argv) < 3: 304 | print('require secret parameter') 305 | return 1 306 | key = argv[2] 307 | print(generate_code(key)) 308 | return 0 309 | if cmd in ('-l', '--list'): 310 | if len(argv) < 3: 311 | print('require file name') 312 | return 1 313 | name = argv[2] 314 | if '~' in name: 315 | name = os.path.expanduser(name) 316 | name = os.path.abspath(name) 317 | if not os.path.exists(name): 318 | print('can not read: %s'%name) 319 | return 255 320 | cont = False 321 | if len(argv) >= 4: 322 | if argv[3] in ('-', '-c', '--continue'): 323 | cont = True 324 | config = load_ini(name) 325 | keys = [] 326 | for key in config: 327 | text = str(key) 328 | try: 329 | num = int(key) 330 | text = num 331 | except: 332 | pass 333 | keys.append((text, key)) 334 | keys.sort() 335 | table = [] 336 | for _, key in keys: 337 | cfg = config[key] 338 | if not cfg: 339 | continue 340 | if 'secret' not in cfg: 341 | continue 342 | secret = cfg['secret'].strip() 343 | user = cfg.get('user', '').strip() 344 | domain = cfg.get('domain', '').strip() 345 | table.append((secret, user, domain)) 346 | list_code(table, cont) 347 | return 0 348 | print('unknown operation') 349 | return 1 350 | 351 | 352 | 353 | #---------------------------------------------------------------------- 354 | # testing case 355 | #---------------------------------------------------------------------- 356 | if __name__ == '__main__': 357 | def test1(): 358 | key = generate_secret_key() 359 | code = generate_code(key) 360 | print(code) 361 | print(verify_time_based(key, code)) 362 | print(generate_secret_key()) 363 | print(get_otpauth_url('skywind3000', 'uex', key)) 364 | print(get_barcode_url('skywind3000', 'uex', key)) 365 | return 0 366 | def test2(): 367 | key = generate_secret_key() 368 | code = generate_code(key) 369 | argv = [sys.argv[0], '-v', key, code] 370 | main(argv) 371 | print(code) 372 | main([sys.argv[0], '-d', key]) 373 | return 0 374 | def test3(): 375 | rows = [] 376 | rows.append(['ID', 'Name', 'Score']) 377 | rows.append(['1', 'Lin Wei', '10']) 378 | rows.append(['2', 'Zhang Jia', '20']) 379 | rows.append([100, 'Cheng Jing Yi', '100']) 380 | rows.append([102, 'Li Lei', '99']) 381 | print(tabulify(rows, 2)) 382 | # tabulify([[],[]]) 383 | return 0 384 | def test4(): 385 | args = [ sys.argv[0], '-l', '~/.config/googauth.ini', '-c' ] 386 | main(args) 387 | return 0 388 | # test1() 389 | argv = None 390 | # argv = [sys.argv[0], '-v', ''] 391 | # test4() 392 | main(argv) 393 | 394 | 395 | 396 | -------------------------------------------------------------------------------- /script/incparse.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skywind3000/collection/add7802471ed96839312b1e917263beb08bed2c4/script/incparse.py -------------------------------------------------------------------------------- /script/linguist.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | #====================================================================== 4 | # 5 | # linguist.py - 6 | # 7 | # Created by skywind on 2017/03/22 8 | # Last change: 2017/03/22 13:44:42 9 | # 10 | #====================================================================== 11 | import sys, os, time 12 | 13 | # https://www.nodebox.net/code/index.php/Linguistics 14 | 15 | #---------------------------------------------------------------------- 16 | # python 2/3 compatible 17 | #---------------------------------------------------------------------- 18 | if sys.version_info[0] >= 3: 19 | long = int 20 | xrange = range 21 | unicode = str 22 | 23 | 24 | #---------------------------------------------------------------------- 25 | # 词形变换 26 | #---------------------------------------------------------------------- 27 | class WordHelper (object): 28 | 29 | # 初始化 30 | def __init__ (self): 31 | self.__lemmatizer = None 32 | 33 | # 取得 WordNet 的词定义 34 | def definition (self, word, txt = False): 35 | from nltk.corpus import wordnet as wn 36 | syns = wn.synsets(word) 37 | output = [] 38 | for syn in syns: 39 | name = syn.name() 40 | part = name.split('.') 41 | mode = part[1] 42 | output.append((mode, syn.definition())) 43 | if txt: 44 | output = '\n'.join([ (m + ' ' + n) for m, n in output ]) 45 | return output 46 | 47 | # 取得动词的:-ing, -ed, -en, -s 48 | # NodeBox 的 Linguistics 软件包 11487 个动词只能处理 6722 个 49 | def verb_tenses (self, word): 50 | word = word.lower() 51 | if ' ' in word: 52 | return None 53 | import en 54 | if not en.is_verb(word): 55 | return None 56 | tenses = {} 57 | try: 58 | tenses['i'] = en.verb.present_participle(word) 59 | tenses['p'] = en.verb.past(word) 60 | tenses['d'] = en.verb.past_participle(word) 61 | tenses['3'] = en.verb.present(word, person = 3, negate = False) 62 | except: 63 | return None 64 | return tenses 65 | 66 | # 取得所有动词 67 | def all_verbs (self): 68 | import en 69 | words = [] 70 | for n in en.wordnet.all_verbs(): 71 | words.append(n.form) 72 | return words 73 | 74 | # 取得所有副词 75 | def all_adverbs (self): 76 | import en 77 | words = [] 78 | for n in en.wordnet.all_adverbs(): 79 | words.append(n.form) 80 | return words 81 | 82 | # 取得所有形容词 83 | def all_adjectives (self): 84 | import en 85 | words = [] 86 | for n in en.wordnet.all_adjectives(): 87 | words.append(n.form) 88 | return words 89 | 90 | # 取得所有名词 91 | def all_adjectives (self): 92 | import en 93 | words = [] 94 | for n in en.wordnet.all_nouns(): 95 | words.append(n.form) 96 | return words 97 | 98 | # 返回原始单词 99 | def lemmatize (self, word, pos = 'n'): 100 | word = word.lower() 101 | if self.__lemmatizer is None: 102 | from nltk.stem.wordnet import WordNetLemmatizer 103 | self.__lemmatizer = WordNetLemmatizer() 104 | return self.__lemmatizer.lemmatize(word, pos) 105 | 106 | 107 | #---------------------------------------------------------------------- 108 | # global 109 | #---------------------------------------------------------------------- 110 | tools = WordHelper() 111 | 112 | 113 | #---------------------------------------------------------------------- 114 | # testing 115 | #---------------------------------------------------------------------- 116 | if __name__ == '__main__': 117 | def test1(): 118 | for word in ['was', 'gave', 'be', 'bound']: 119 | print('%s -> %s'%(word, tools.lemmatize(word, 'v'))) 120 | return 0 121 | test1() 122 | 123 | 124 | 125 | 126 | -------------------------------------------------------------------------------- /script/literotica.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | #====================================================================== 4 | # 5 | # literotica.py - 6 | # 7 | # Created by skywind on 2017/02/24 8 | # Last change: 2017/02/24 18:10:44 9 | # 10 | #====================================================================== 11 | import sys, time, re 12 | import shell 13 | 14 | 15 | #---------------------------------------------------------------------- 16 | # Literotica 17 | #---------------------------------------------------------------------- 18 | class Literotica (object): 19 | 20 | def __init__ (self, url): 21 | self._url = url 22 | p1 = url.find('?page=') 23 | if p1 >= 0: 24 | self.url = url[:p1] 25 | self._content = shell.request_safe(self._url) 26 | self._extract() 27 | 28 | def _extract (self): 29 | content = self._content 30 | p1 = content.find('
') 31 | self._intro = '' 32 | if p1 < 0: 33 | return -2 34 | p1 = content.find('

', p1) 35 | if p1 < 0: 36 | return -2 37 | p2 = content.find('

', p1) 38 | if p2 < 0: 39 | return -2 40 | title = content[p1 + 4:p2] 41 | self._intro = shell.html2text(title).strip() + '\n\n\n' 42 | p1 = content.find('', p1) 45 | if p2 < 0: 46 | return -1 47 | count = 0 48 | self._index = [] 49 | while p1 < p2: 50 | p1 = content.find('