feat(saolei): 命令行交互式玩扫雷(无需 VGA 显示器)
- srcs/tb/tb_saolei_play.v:受 cmd.txt 驱动的交互式 TB,每 100us 轮询一个按键字符, 每 1ms 把显存 dump 到 now.txt - tools/play_saolei.tcl:xsim 里 k c / k u / k f 一步一按键,自动把 now.txt 画到终端 - saolei/tools/fb2png.py:新增 --term,用 ANSI 24 位色在终端画 40x30 棋盘,光标格反色 - .gitignore:放行 create_project_saolei.tcl 实测:k c 进关卡、k uuuu 光标精确移动 4 格、k c 首击展开出数字 1/2
This commit is contained in:
+68
-20
@@ -3,6 +3,7 @@
|
||||
|
||||
用法:
|
||||
py saolei/tools/fb2png.py <dump.txt> [输出.png]
|
||||
py saolei/tools/fb2png.py --term <dump.txt> # 只在终端打印彩色棋盘(交互玩扫雷用)
|
||||
|
||||
dump 格式(tb_saolei.v 里的 dump_fb 任务生成):
|
||||
CURSOR <row> <col>
|
||||
@@ -101,15 +102,77 @@ def parse_dump(path):
|
||||
return cur, ctrl, fb
|
||||
|
||||
|
||||
def cell_char(g):
|
||||
"""字形号 -> 一个可见字符(字形 1..8 就是数字 1..8)。"""
|
||||
if g in GLYPH_CH:
|
||||
return GLYPH_CH[g]
|
||||
if 1 <= g <= 8:
|
||||
return chr(ord('0') + g)
|
||||
return ' '
|
||||
|
||||
|
||||
def dump_note(path):
|
||||
"""把 dump 开头的 TIME / CURSOR / STATS 拼成一行,当终端画面的标题。"""
|
||||
note = []
|
||||
try:
|
||||
with open(path, encoding="utf-8", errors="replace") as f:
|
||||
for line in f:
|
||||
if line.startswith(("TIME", "CURSOR", "STATS")):
|
||||
note.append(line.strip())
|
||||
elif line.startswith("FB"):
|
||||
break
|
||||
except OSError:
|
||||
pass
|
||||
return " ".join(note)
|
||||
|
||||
|
||||
def draw_term(fb, cur, cursor_box, note=""):
|
||||
"""在终端用 ANSI 24 位颜色画 40x30 棋盘(每格一个字符),光标格反色。"""
|
||||
try: # Windows 控制台默认不是 UTF-8
|
||||
sys.stdout.reconfigure(encoding="utf-8")
|
||||
except Exception:
|
||||
pass
|
||||
out = ["\x1b[H\x1b[2J"] # 回左上角 + 清屏(连续刷新不闪屏残留)
|
||||
if note:
|
||||
out.append(" " + note + "\n")
|
||||
out.append(" " + "".join(str(x % 10) for x in range(COLS)) + "\n")
|
||||
for y in range(ROWS):
|
||||
cells = []
|
||||
for x in range(COLS):
|
||||
cell = fb[y * COLS + x]
|
||||
g, c = cell & 0xF, (cell >> 4) & 0x7
|
||||
r, gg, b = PAL[c]
|
||||
ch = cell_char(g)
|
||||
if cursor_box and (x, y) == cur:
|
||||
cells.append("\x1b[7m\x1b[38;2;%d;%d;%dm%s" % (r * 17, gg * 17, b * 17,
|
||||
ch if ch != ' ' else '_'))
|
||||
else:
|
||||
cells.append("\x1b[38;2;%d;%d;%dm%s" % (r * 17, gg * 17, b * 17, ch))
|
||||
out.append("%2d " % y + "".join(cells) + "\x1b[0m\n")
|
||||
out.append("\x1b[0m")
|
||||
out.append("图例: #=未揭开 P=旗 *=雷 X=爆炸 ?=问号 1..8=数字 空格=已揭开的空地;反色格 = 光标\n")
|
||||
sys.stdout.write("".join(out))
|
||||
sys.stdout.flush()
|
||||
|
||||
|
||||
def main():
|
||||
if len(sys.argv) < 2:
|
||||
args = list(sys.argv[1:])
|
||||
term_only = "--term" in args
|
||||
if term_only:
|
||||
args.remove("--term")
|
||||
if not args:
|
||||
raise SystemExit(__doc__)
|
||||
src = sys.argv[1]
|
||||
dst = sys.argv[2] if len(sys.argv) > 2 else src.rsplit(".", 1)[0] + ".png"
|
||||
src = args[0]
|
||||
dst = args[1] if len(args) > 1 else src.rsplit(".", 1)[0] + ".png"
|
||||
cur, ctrl, fb = parse_dump(src)
|
||||
|
||||
bg = (ctrl >> 4) & 0x7
|
||||
cursor_box = bool(ctrl & 1) and cur[0] is not None
|
||||
|
||||
if term_only:
|
||||
draw_term(fb, cur, cursor_box, dump_note(src))
|
||||
return
|
||||
|
||||
print("光标 = (%s, %s) 光标可见 = %s 背景色 = %d"
|
||||
% (cur[0], cur[1], bool(ctrl & 1), bg))
|
||||
|
||||
@@ -135,23 +198,8 @@ def main():
|
||||
write_png(dst, COLS * CELL_PX, ROWS * CELL_PX, rows)
|
||||
print("已生成 %s(%dx%d)" % (dst, COLS * CELL_PX, ROWS * CELL_PX))
|
||||
|
||||
# ---- 终端 ASCII 图 ----
|
||||
print("+" + "-" * COLS + "+")
|
||||
for y in range(ROWS):
|
||||
line = ""
|
||||
for x in range(COLS):
|
||||
cell = fb[y * COLS + x]
|
||||
g, c = cell & 0xF, (cell >> 4) & 0x7
|
||||
if cursor_box and (x, y) == cur:
|
||||
line += "["
|
||||
elif g in GLYPH_CH:
|
||||
line += GLYPH_CH[g]
|
||||
else:
|
||||
line += chr(ord('0') + g) # 字形 1..8 就是数字 1..8
|
||||
line = line.replace("[", "[]" * 1) # 光标只提示左边界,避免打乱列数
|
||||
print("|" + (line + " " * COLS)[:COLS] + "|")
|
||||
print("+" + "-" * COLS + "+")
|
||||
print("图例: #=未揭开 P=旗 *=雷 X=爆炸 ?=问号 -=横线 ==实心 1..8=数字")
|
||||
# ---- 终端 ASCII 图(彩色版;PNG 已经写好了,这里只是方便快速看一眼)----
|
||||
draw_term(fb, cur, cursor_box, dump_note(src))
|
||||
|
||||
|
||||
main()
|
||||
|
||||
Reference in New Issue
Block a user