mirror of
https://github.com/wahyd4/one-knowledge.git
synced 2026-08-08 20:59:08 +10:00
Sync: 4 new posts from Links App (2026-06-18 06:33)
This commit is contained in:
+1
-1
@@ -1 +1 @@
|
||||
2026-06-16T17:01:11.214068+10:00
|
||||
2026-06-17T17:01:07.811986+10:00
|
||||
@@ -0,0 +1,26 @@
|
||||
---
|
||||
title: "2026-06-17-technology"
|
||||
created: 2026-06-17
|
||||
updated: 2026-06-17
|
||||
type: summary
|
||||
tags: [newsletter, technology]
|
||||
external: http://go/ui/posts/2776/
|
||||
---
|
||||
|
||||
** Tech news**
|
||||
- [Nvidia 计划首次 AI 热潮后发行至少 200 亿美元债券](https://www.cnbc.com/2026/06/15/nvidia-plans-to-raise-about-20-billion-first-debt-sale-in-ai-boom.html)
|
||||
自2021年以来首次大规模债务融资,芯片巨头市值已远非昔日可比。
|
||||
- [Anthropic 与特朗普政府协商恢复强大 AI 模型访问权限](https://www.wsj.com/tech/ai/anthropic-trump-officials-seek-deal-on-restoring-powerful-model-access-d9c4ffee?mod=rss_Technology)
|
||||
双方正快速推进解决,这是 Anthropic 撤回“Fable”和“Mythos”模型后的首次公开表态。
|
||||
- [高通 CEO 称 AI 代理将取代应用程序,正研发 40 款新 AI 设备](https://www.cnbc.com/2026/06/16/qualcomm-ceo-ai-devices-agents.html)
|
||||
高通看好智能眼镜等设备,其潜力或堪比智能手机;同时被视作稀有的 AI 芯片价值股。
|
||||
- [DeepSeek 融资超 74 亿美元,成中国最具价值 AI 初创](https://www.wsj.com/tech/ai/deepseek-becomes-chinas-most-valuable-ai-startup-after-over-7-4-billion-fundraise-78ef64c0?mod=rss_Technology)
|
||||
DeepSeek 首轮融资规模巨大,支持其高昂的 AI 研发成本。
|
||||
- [SpaceX 以 600 亿美元收购 AI 编程初创公司 Cursor](https://www.cnbc.com/2026/06/16/spacex-spcx-cursor-acquisition-ipo.html)
|
||||
交易旨在增强其与 Anthropic、OpenAI 等对手在 AI 编程工具领域的竞争力,距其历史性 IPO 仅数日。
|
||||
- [SpaceX 二把手 Gwynne Shotwell 如何助其成为 IPO 巨头](https://www.cnbc.com/2026/06/16/gwynne-shotwell-spacex-elon-musk-ipo.html)
|
||||
CNBC 专访多位与 SpaceX 总裁兼 COO 共事过的人士,揭示其关键作用。
|
||||
- [Mobileye 计划 2027 年在美国推出机器人出租车服务](https://www.cnbc.com/2026/06/16/self-driving-tech-supplier-mobileye-targets-us-robotaxi-launch-in-2027.html)
|
||||
自动驾驶技术供应商 Mobileye 宣布其自动驾驶出租车服务上线时间表。
|
||||
- [强生 CEO 称借助 AI 攻克特定癌症是现实目标](https://www.wsj.com/health/healthcare/johnson-johnson-ceo-says-cure-for-certain-cancers-is-realistic-goal-234d2767?mod=rss_Technology)
|
||||
AI 正加速药物研发,但其对利润的具体提升程度尚待观察。
|
||||
@@ -0,0 +1,26 @@
|
||||
---
|
||||
title: "2026-06-17-victoria"
|
||||
created: 2026-06-17
|
||||
updated: 2026-06-17
|
||||
type: summary
|
||||
tags: [local_news, newsletter]
|
||||
external: http://go/ui/posts/2777/
|
||||
---
|
||||
|
||||
# 焦点新闻摘要
|
||||
- [澳洲联储暂停加息,但未排除未来继续上调;楼市价格下跌引关注](https://www.sbs.com.au/news/live-blog/rba-june-2026-interest-rates-decision-live/ntpgs9xc6)
|
||||
澳洲储备银行(RBA)今年首次暂停加息,但表示通胀仍然过高。同时,市场正密切关注房价走势,部分人士期待在市场底部购入,引发了关于房价未来跌幅的讨论。
|
||||
- [美国军方计划在澳洲境内建立大型战备物资储备库](https://www.sbs.com.au/news/article/us-military-plans-major-war-stockpile-on-australian-soil/rbg8qyb0q)
|
||||
作为应对中国崛起的一项亚洲地区防御战略,美国军方计划在澳大利亚建立大规模武器储备。
|
||||
- [墨尔本市中心一高层托儿所儿童面临12层高坠落风险,险酿灾难](https://www.theage.com.au/national/victoria/could-have-been-catastrophic-children-at-risk-of-12-storey-plunge-from-high-rise-childcare-centre-20260616-p60781.html?ref=rss&utm_medium=rss&utm_source=rss_national_victoria)
|
||||
墨尔本市中心一间高层托儿中心的儿童曾面临从12层屋顶边缘坠落的危险,情况“可能造成灾难性后果”。
|
||||
- [致命“自由分娩”案:助产师为自己的行为辩护,称未考虑呼叫救护车](https://www.theage.com.au/national/victoria/no-way-i-was-going-to-call-an-ambulance-doula-at-fatal-free-birth-defends-actions-20260616-p6073x.html?ref=rss&utm_medium=rss&utm_source=rss_national_victoria)
|
||||
维州一名助产师在死因裁判官法庭上表示,她起初认为临终的孕妇Stacey Warnecke只是恐慌发作,从未想过呼叫救护车。
|
||||
- [被判46年徒刑的“掠夺性”教师,此前已被揭露对学生进行性侵](https://www.theage.com.au/national/victoria/predatory-teacher-serving-46-years-for-sadistic-abuse-also-assaulted-his-students-20260616-p607e5.html?ref=rss&utm_medium=rss&utm_source=rss_national_victoria)
|
||||
这名前教师因儿童性虐待案已被判处澳洲最长刑期,如今更多针对其学生的袭击事件被揭露。
|
||||
- [澳洲记录上“最致命的一年”揭示隐藏危机正在恶化](https://www.sbs.com.au/news/article/australias-deadliest-year-on-record- exposes-a-worsening-hidden-crisis/c4gnrk0xd)
|
||||
最新数据显示,澳大利亚经历了一个悲剧性的里程碑,预示着一个日益恶化的隐藏危机。
|
||||
- [警方相信Dezi Freeman已前往新州,更多逮捕行动正在进行](https://www.theage.com.au/national/victoria/police-believe-dezi-freeman-travelled-to-nsw-more-arrests-made-20260616-p607c5.html?ref=rss&utm_medium=rss&utm_source=rss_national_victoria)
|
||||
在涉嫌杀害警员的凶手Dezi Freeman在边境枪战中身亡近三个月后,警方仍在深入调查其216天逃亡期间的行踪,并逮捕了更多涉案人员。
|
||||
- [世界杯裁判肖恩·埃文斯被澄清“白人至上”手势指控](https://www.sbs.com.au/news/article/shaun-evans-world-cup-referee-white-power-gesture-cleared/hdrhammtw)
|
||||
国际足联(FIFA)在肖恩·埃文斯否认故意做出此手势后,将其澄清,埃文斯称那只是“不自主的抽搐”。
|
||||
@@ -0,0 +1,589 @@
|
||||
---
|
||||
title: "GLM 5.2 pacman"
|
||||
created: 2026-06-17
|
||||
updated: 2026-06-17
|
||||
type: summary
|
||||
tags: [AI]
|
||||
external: http://go/ui/posts/2778/
|
||||
---
|
||||
|
||||
Here's the complete `pacman.py` implementation:
|
||||
|
||||
```python
|
||||
#!/usr/bin/env python3
|
||||
"""Pac-Man — Terminal Edition. Run: python3 pacman.py"""
|
||||
|
||||
import curses
|
||||
import time
|
||||
import random
|
||||
|
||||
# ─── Maze ───────────────────────────────────────────────────────────
|
||||
# 31 rows × 28 cols. '#' wall, '.' dot, 'o' power pellet,
|
||||
# ' ' empty, 'G' ghost-house door.
|
||||
MAZE_TEMPLATE = [
|
||||
"############################",
|
||||
"#............##............#",
|
||||
"#.####.#####.##.#####.####.#",
|
||||
"#o####.#####.##.#####.####o#",
|
||||
"#.####.#####.##.#####.####.#",
|
||||
"#..........................#",
|
||||
"#.####.##.########.##.####.#",
|
||||
"#.####.##.########.##.####.#",
|
||||
"#......##....##....##......#",
|
||||
"######.##### ## #####.######",
|
||||
" #.##### ## #####.# ",
|
||||
" #.## ##.# ",
|
||||
" #.## ###--### ##.# ",
|
||||
"######.## # # ##.######",
|
||||
" # # ",
|
||||
"######.## # # ##.######",
|
||||
" #.## ######## ##.# ",
|
||||
" #.## ##.# ",
|
||||
" #.## ######## ##.# ",
|
||||
"######.## ######## ##.######",
|
||||
"#............##............#",
|
||||
"#.####.#####.##.#####.####.#",
|
||||
"#.####.#####.##.#####.####.#",
|
||||
"#o..##.......##.......##..o#",
|
||||
"###.##.##.########.##.##.###",
|
||||
"###.##.##.########.##.##.###",
|
||||
"#......##....##....##......#",
|
||||
"#.##########.##.##########.#",
|
||||
"#.##########.##.##########.#",
|
||||
"#..........................#",
|
||||
"############################",
|
||||
]
|
||||
|
||||
ROWS = 31
|
||||
COLS = 28
|
||||
|
||||
# Ghost-house door position (row 12, col 14 → the two '-' cells become 'G')
|
||||
# We'll patch the template after copying.
|
||||
|
||||
# Directions: (dr, dc)
|
||||
UP = (-1, 0)
|
||||
DOWN = (1, 0)
|
||||
LEFT = (0, -1)
|
||||
RIGHT = (0, 1)
|
||||
DIRS = [UP, DOWN, LEFT, RIGHT]
|
||||
OPPOSITE = {UP: DOWN, DOWN: UP, LEFT: RIGHT, RIGHT: UP}
|
||||
|
||||
# ─── Ghost ──────────────────────────────────────────────────────────
|
||||
class Ghost:
|
||||
COLORS = {0: 1, 1: 2, 2: 3, 3: 4} # Blinky, Pinky, Inky, Clyde
|
||||
GLYPH = 'ᗣ'
|
||||
EATEN = '◌'
|
||||
|
||||
def __init__(self, idx, home_r, home_c, start_r, start_c):
|
||||
self.idx = idx
|
||||
self.color = self.COLORS[idx]
|
||||
self.home_r = home_r
|
||||
self.home_c = home_c
|
||||
self.start_r = start_r
|
||||
self.start_c = start_c
|
||||
self.reset()
|
||||
|
||||
def reset(self):
|
||||
self.r = self.start_r
|
||||
self.c = self.start_c
|
||||
self.dir = UP
|
||||
self.frightened = False
|
||||
self.fright_timer = 0.0
|
||||
self.eaten = False
|
||||
self.spawn_delay = float(self.idx * 2) # 0,2,4,6 s
|
||||
|
||||
# ── helpers ────────────────────────────────────────────────────
|
||||
@staticmethod
|
||||
def _walkable(maze, r, c):
|
||||
"""Ghost-walkable: not a wall."""
|
||||
return maze[r][c] != '#'
|
||||
|
||||
def _blink(self):
|
||||
"""True when frightened & should show white (last 2 s)."""
|
||||
return (self.frightened
|
||||
and self.fright_timer < 2.0
|
||||
and int(self.fright_timer * 3) % 2 == 0)
|
||||
|
||||
# ── movement ───────────────────────────────────────────────────
|
||||
def move(self, maze, pac_r, pac_c, dt):
|
||||
if self.spawn_delay > 0:
|
||||
self.spawn_delay -= dt
|
||||
return
|
||||
|
||||
# Frightened timer
|
||||
if self.frightened:
|
||||
self.fright_timer -= dt
|
||||
if self.fright_timer <= 0:
|
||||
self.frightened = False
|
||||
|
||||
# Target cell
|
||||
if self.eaten:
|
||||
tr, tc = self.home_r, self.home_c
|
||||
# arrived home?
|
||||
if self.r == self.home_r and self.c == self.home_c:
|
||||
self.eaten = False
|
||||
self.frightened = False
|
||||
self.fright_timer = 0
|
||||
elif self.frightened:
|
||||
tr, tc = random.randint(0, ROWS-1), random.randint(0, COLS-1)
|
||||
else:
|
||||
offsets = [(0, 0), (2, 2), (-2, 2), (2, -2)]
|
||||
dr, dc = offsets[self.idx]
|
||||
tr, tc = pac_r + dr, pac_c + dc
|
||||
|
||||
# Candidate directions (no reverse), shuffled for tie-break
|
||||
cand = [d for d in DIRS if d != OPPOSITE.get(self.dir)]
|
||||
random.shuffle(cand)
|
||||
|
||||
best, best_d = None, None
|
||||
for d in cand:
|
||||
nr, nc = self.r + d[0], (self.c + d[1]) % COLS
|
||||
if not self._walkable(maze, nr, nc):
|
||||
continue
|
||||
dist = (nr - tr) ** 2 + (nc - tc) ** 2
|
||||
if best_d is None or dist < best_d:
|
||||
best_d = dist
|
||||
best = d
|
||||
|
||||
if best is None: # dead end → allow reverse
|
||||
for d in DIRS:
|
||||
nr, nc = self.r + d[0], (self.c + d[1]) % COLS
|
||||
if self._walkable(maze, nr, nc):
|
||||
best = d
|
||||
break
|
||||
|
||||
if best is not None:
|
||||
self.r += best[0]
|
||||
self.c = (self.c + best[1]) % COLS
|
||||
self.dir = best
|
||||
|
||||
|
||||
# ─── PacmanGame ─────────────────────────────────────────────────────
|
||||
class PacmanGame:
|
||||
PAC_INTERVAL = 0.14
|
||||
GHOST_INTERVAL = 0.22
|
||||
BLINK_INTERVAL = 0.35
|
||||
FRIGHT_TIME = 7.0
|
||||
|
||||
PAC_ARROWS = {UP: 'ᐱ', DOWN: 'ᐯ', LEFT: 'ᐸ', RIGHT: 'ᐳ'}
|
||||
|
||||
def __init__(self, stdscr):
|
||||
self.stdscr = stdscr
|
||||
self._setup_colors()
|
||||
self.score = 0
|
||||
self.lives = 3
|
||||
self.level = 1
|
||||
self.paused = False
|
||||
self.running = True
|
||||
|
||||
# ── colour setup ───────────────────────────────────────────────
|
||||
def _setup_colors(self):
|
||||
curses.use_default_colors()
|
||||
curses.init_pair(1, curses.COLOR_RED, -1)
|
||||
curses.init_pair(2, curses.COLOR_MAGENTA, -1)
|
||||
curses.init_pair(3, curses.COLOR_CYAN, -1)
|
||||
curses.init_pair(4, curses.COLOR_YELLOW, -1)
|
||||
curses.init_pair(5, curses.COLOR_YELLOW, -1)
|
||||
curses.init_pair(6, curses.COLOR_BLUE, -1)
|
||||
curses.init_pair(7, curses.COLOR_WHITE, -1)
|
||||
curses.init_pair(8, curses.COLOR_GREEN, -1)
|
||||
|
||||
# ── game init ──────────────────────────────────────────────────
|
||||
def _init_game(self):
|
||||
self.maze = [list(row) for row in MAZE_TEMPLATE]
|
||||
|
||||
# Mark ghost-house door cells (row 12, cols 13-14) as 'G'
|
||||
for c in (13, 14):
|
||||
if 0 <= 12 < ROWS and 0 <= c < COLS:
|
||||
self.maze[12][c] = 'G'
|
||||
|
||||
self.score = 0
|
||||
self.lives = 3
|
||||
self.level = 1
|
||||
self.dots_eaten = 0
|
||||
self.total_dots = sum(
|
||||
1 for r in range(ROWS) for c in range(COLS)
|
||||
if self.maze[r][c] in ('.', 'o')
|
||||
)
|
||||
|
||||
# Pac-Man
|
||||
self.pac_r = 23
|
||||
self.pac_c = 14
|
||||
self.pac_dir = LEFT
|
||||
self.next_dir = LEFT
|
||||
|
||||
# Ghosts — all start inside the house
|
||||
self.ghosts = [
|
||||
Ghost(0, 14, 13, 14, 13), # Blinky (door cell-ish)
|
||||
Ghost(1, 14, 13, 14, 12), # Pinky
|
||||
Ghost(2, 14, 14, 15, 12), # Inky
|
||||
Ghost(3, 14, 14, 15, 14), # Clyde
|
||||
]
|
||||
|
||||
self.last_pac = time.time()
|
||||
self.last_ghost = time.time()
|
||||
self.last_blink = time.time()
|
||||
self.blink_on = True
|
||||
self.pac_mouth = True
|
||||
|
||||
# ── public entry ───────────────────────────────────────────────
|
||||
def run(self):
|
||||
self._show_splash()
|
||||
while self.running:
|
||||
self._init_game()
|
||||
self._play()
|
||||
if not self._game_over_screen():
|
||||
break
|
||||
|
||||
# ── splash ─────────────────────────────────────────────────────
|
||||
def _show_splash(self):
|
||||
s = self.stdscr
|
||||
s.nodelay(False)
|
||||
s.clear()
|
||||
sh, sw = s.getmaxyx()
|
||||
|
||||
title_lines = [
|
||||
"██████╗ █████╗ ██████╗ █████╗ ██╗ ██╗",
|
||||
"██╔══██╗██╔══██╗██╔══██╗██╔══██╗██║ ██║",
|
||||
"██████╔╝███████║██████╔╝███████║██║ ██║",
|
||||
"██╔═══╝ ██╔══██║██╔══██╗██╔══██║██║ ██║",
|
||||
"██║ ██║ ██║██████╔╝██║ ██║╚██████╔╝",
|
||||
"╚═╝ ╚═╝ ╚═╝╚═════╝ ╚═╝ ╚═╝ ╚═════╝ ",
|
||||
]
|
||||
top = max(0, (sh - 12) // 2)
|
||||
for i, line in enumerate(title_lines):
|
||||
col = max(0, (sw - len(line)) // 2)
|
||||
try:
|
||||
s.addstr(top + i, col, line, curses.color_pair(5) | curses.A_BOLD)
|
||||
except curses.error:
|
||||
pass
|
||||
|
||||
info_top = top + len(title_lines) + 2
|
||||
ghosts_line = (
|
||||
"Blinky ᗣ Pinky ᗣ Inky ᗣ Clyde ᗣ"
|
||||
)
|
||||
ctrl_line = "Arrow keys / WASD: move P: pause Q: quit"
|
||||
start_line = "Press any key to start!"
|
||||
|
||||
for i, (line, pair) in enumerate([
|
||||
(ghosts_line, 7),
|
||||
(ctrl_line, 7),
|
||||
(start_line, 8),
|
||||
]):
|
||||
col = max(0, (sw - len(line)) // 2)
|
||||
try:
|
||||
s.addstr(info_top + i, col, line,
|
||||
curses.color_pair(pair) | curses.A_BOLD)
|
||||
except curses.error:
|
||||
pass
|
||||
|
||||
s.refresh()
|
||||
s.getch()
|
||||
|
||||
# ── main play loop ─────────────────────────────────────────────
|
||||
def _play(self):
|
||||
s = self.stdscr
|
||||
s.nodelay(True)
|
||||
curses.curs_set(0)
|
||||
now = time.time()
|
||||
self.last_pac = now
|
||||
self.last_ghost = now
|
||||
self.last_blink = now
|
||||
|
||||
playing = True
|
||||
while playing:
|
||||
now = time.time()
|
||||
dt = now - self._last_frame if hasattr(self, '_last_frame') else 0.016
|
||||
self._last_frame = now
|
||||
|
||||
if self._handle_input() == 'QUIT':
|
||||
self.running = False
|
||||
return
|
||||
|
||||
if not self.paused:
|
||||
# Pac-Man
|
||||
if now - self.last_pac >= self.PAC_INTERVAL:
|
||||
self._move_pacman()
|
||||
self.last_pac = now
|
||||
self.pac_mouth = not self.pac_mouth
|
||||
|
||||
# Ghosts
|
||||
if now - self.last_ghost >= self.GHOST_INTERVAL:
|
||||
for g in self.ghosts:
|
||||
g.move(self.maze, self.pac_r, self.pac_c,
|
||||
self.GHOST_INTERVAL)
|
||||
self.last_ghost = now
|
||||
|
||||
# Blink timer
|
||||
if now - self.last_blink >= self.BLINK_INTERVAL:
|
||||
self.blink_on = not self.blink_on
|
||||
self.last_blink = now
|
||||
|
||||
result = self._check_collisions()
|
||||
if result == 'LOSE':
|
||||
playing = False
|
||||
continue
|
||||
if self._check_win():
|
||||
playing = False
|
||||
self.won = True
|
||||
continue
|
||||
|
||||
self._draw()
|
||||
time.sleep(0.02)
|
||||
|
||||
# ── input ──────────────────────────────────────────────────────
|
||||
def _handle_input(self):
|
||||
s = self.stdscr
|
||||
while True:
|
||||
ch = s.getch()
|
||||
if ch == -1:
|
||||
break
|
||||
if ch in (curses.KEY_UP, ord('w'), ord('W')):
|
||||
self.next_dir = UP
|
||||
elif ch in (curses.KEY_DOWN, ord('s'), ord('S')):
|
||||
self.next_dir = DOWN
|
||||
elif ch in (curses.KEY_LEFT, ord('a'), ord('A')):
|
||||
self.next_dir = LEFT
|
||||
elif ch in (curses.KEY_RIGHT, ord('d'), ord('D')):
|
||||
self.next_dir = RIGHT
|
||||
elif ch in (ord('p'), ord('P')):
|
||||
self.paused = not self.paused
|
||||
elif ch in (ord('q'), ord('Q')):
|
||||
return 'QUIT'
|
||||
return None
|
||||
|
||||
# ── Pac-Man movement ───────────────────────────────────────────
|
||||
def _move_pacman(self):
|
||||
maze = self.maze
|
||||
|
||||
def try_dir(d):
|
||||
nr = self.pac_r + d[0]
|
||||
nc = (self.pac_c + d[1]) % COLS
|
||||
cell = maze[nr][nc]
|
||||
if cell == '#' or cell == 'G':
|
||||
return False
|
||||
return True
|
||||
|
||||
d = self.next_dir if try_dir(self.next_dir) else (
|
||||
self.pac_dir if try_dir(self.pac_dir) else None
|
||||
)
|
||||
if d is None:
|
||||
return
|
||||
|
||||
self.pac_r += d[0]
|
||||
self.pac_c = (self.pac_c + d[1]) % COLS
|
||||
self.pac_dir = d
|
||||
|
||||
cell = self.maze[self.pac_r][self.pac_c]
|
||||
if cell == '.':
|
||||
self.maze[self.pac_r][self.pac_c] = ' '
|
||||
self.score += 10
|
||||
self.dots_eaten += 1
|
||||
elif cell == 'o':
|
||||
self.maze[self.pac_r][self.pac_c] = ' '
|
||||
self.score += 50
|
||||
self.dots_eaten += 1
|
||||
for g in self.ghosts:
|
||||
if not g.eaten:
|
||||
g.frightened = True
|
||||
g.fright_timer = self.FRIGHT_TIME
|
||||
|
||||
# ── collisions ─────────────────────────────────────────────────
|
||||
def _check_collisions(self):
|
||||
for g in self.ghosts:
|
||||
if g.r == self.pac_r and g.c == self.pac_c:
|
||||
if g.eaten:
|
||||
continue
|
||||
if g.frightened:
|
||||
g.eaten = True
|
||||
g.frightened = False
|
||||
self.score += 200
|
||||
else:
|
||||
self._lose_life()
|
||||
return 'LOSE'
|
||||
return None
|
||||
|
||||
def _lose_life(self):
|
||||
self.lives -= 1
|
||||
if self.lives <= 0:
|
||||
self.won = False
|
||||
return
|
||||
# reset positions
|
||||
self.pac_r, self.pac_c = 23, 14
|
||||
self.pac_dir = LEFT
|
||||
self.next_dir = LEFT
|
||||
for g in self.ghosts:
|
||||
g.reset()
|
||||
self._draw()
|
||||
time.sleep(1.0)
|
||||
|
||||
def _check_win(self):
|
||||
if self.dots_eaten >= self.total_dots:
|
||||
self.won = True
|
||||
return True
|
||||
return False
|
||||
|
||||
# ── rendering ──────────────────────────────────────────────────
|
||||
def _addch(self, r, c, ch, attr=0):
|
||||
try:
|
||||
self.stdscr.addch(r, c, ch, attr)
|
||||
except curses.error:
|
||||
pass
|
||||
|
||||
def _draw(self):
|
||||
s = self.stdscr
|
||||
sh, sw = s.getmaxyx()
|
||||
offset_r = max(0, (sh - ROWS - 3) // 2)
|
||||
offset_c = max(0, (sw - COLS) // 2)
|
||||
|
||||
s.clear()
|
||||
|
||||
# maze
|
||||
for r in range(ROWS):
|
||||
for c in range(COLS):
|
||||
cell = self.maze[r][c]
|
||||
R, C = offset_r + r, offset_c + c
|
||||
if cell == '#':
|
||||
self._addch(R, C, '█', curses.color_pair(7) | curses.A_DIM)
|
||||
elif cell == '.':
|
||||
self._addch(R, C, '·', curses.color_pair(7))
|
||||
elif cell == 'o':
|
||||
self._addch(R, C, '●', curses.color_pair(7) | curses.A_BOLD)
|
||||
elif cell == 'G':
|
||||
self._addch(R, C, '─', curses.color_pair(7) | curses.A_DIM)
|
||||
# ' ' → nothing
|
||||
|
||||
# ghosts
|
||||
for g in self.ghosts:
|
||||
R, C = offset_r + g.r, offset_c + g.c
|
||||
if g.eaten:
|
||||
self._addch(R, C, Ghost.EATEN, curses.color_pair(7) | curses.A_BOLD)
|
||||
elif g.frightened:
|
||||
if g._blink():
|
||||
attr = curses.color_pair(7) | curses.A_BOLD
|
||||
else:
|
||||
attr = curses.color_pair(6) | curses.A_BOLD
|
||||
self._addch(R, C, Ghost.GLYPH, attr)
|
||||
else:
|
||||
self._addch(R, C, Ghost.GLYPH,
|
||||
curses.color_pair(g.color) | curses.A_BOLD)
|
||||
|
||||
# Pac-Man
|
||||
R, C = offset_r + self.pac_r, offset_c + self.pac_c
|
||||
if self.pac_mouth:
|
||||
glyph = self.PAC_ARROWS.get(self.pac_dir, 'O')
|
||||
else:
|
||||
glyph = 'O'
|
||||
self._addch(R, C, glyph, curses.color_pair(5) | curses.A_BOLD)
|
||||
|
||||
# HUD
|
||||
hud_r = offset_r + ROWS + 1
|
||||
lives_str = ' '.join(['●'] * max(0, self.lives))
|
||||
hud = f"SCORE: {self.score:06d} LIVES: {lives_str:<5} LEVEL: {self.level}"
|
||||
if self.paused:
|
||||
hud += " [PAUSED]"
|
||||
hud_c = max(0, (sw - len(hud)) // 2)
|
||||
try:
|
||||
s.addstr(hud_r, hud_c, hud, curses.color_pair(8) | curses.A_BOLD)
|
||||
except curses.error:
|
||||
pass
|
||||
|
||||
s.refresh()
|
||||
|
||||
# ── game-over screen ───────────────────────────────────────────
|
||||
def _center_msg(self, r, msg, attr=0):
|
||||
s = self.stdscr
|
||||
sh, sw = s.getmaxyx()
|
||||
c = max(0, (sw - len(msg)) // 2)
|
||||
try:
|
||||
s.addstr(r, c, msg, attr)
|
||||
except curses.error:
|
||||
pass
|
||||
|
||||
def _game_over_screen(self):
|
||||
"""Return True to play again, False to exit."""
|
||||
s = self.stdscr
|
||||
s.nodelay(False)
|
||||
s.clear()
|
||||
sh, sw = s.getmaxyx()
|
||||
mid = sh // 2
|
||||
|
||||
if getattr(self, 'won', False):
|
||||
self._center_msg(mid - 2, "YOU WIN! 🎉",
|
||||
curses.color_pair(8) | curses.A_BOLD)
|
||||
else:
|
||||
self._center_msg(mid - 2, "GAME OVER",
|
||||
curses.color_pair(1) | curses.A_BOLD)
|
||||
|
||||
self._center_msg(mid, f"Final Score: {self.score}",
|
||||
curses.color_pair(7) | curses.A_BOLD)
|
||||
self._center_msg(mid + 2, "Play again? (Y / N)",
|
||||
curses.color_pair(7))
|
||||
|
||||
s.refresh()
|
||||
while True:
|
||||
ch = s.getch()
|
||||
if ch in (ord('y'), ord('Y')):
|
||||
return True
|
||||
if ch in (ord('n'), ord('N'), ord('q'), ord('Q')):
|
||||
return False
|
||||
|
||||
|
||||
# ─── entry point ────────────────────────────────────────────────────
|
||||
def main(stdscr):
|
||||
game = PacmanGame(stdscr)
|
||||
game.run()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
curses.wrapper(main)
|
||||
```
|
||||
|
||||
## Quick reference
|
||||
|
||||
| Item | Detail |
|
||||
|------|--------|
|
||||
| **Run** | `python3 pacman.py` |
|
||||
| **Move** | Arrow keys or WASD |
|
||||
| **Pause** | `P` |
|
||||
| **Quit** | `Q` |
|
||||
| **Min terminal** | 80 × 34 |
|
||||
|
||||
## How it works
|
||||
|
||||
<details>
|
||||
<summary><b>Maze & cells</b></summary>
|
||||
|
||||
`MAZE_TEMPLATE` is a 31×28 grid of strings. At `_init_game()` it's deep-copied to a list-of-lists so dots/pellets can be consumed. The two `-` cells at row 12, cols 13–14 are patched to `'G'` (ghost-house door). Row 14's left/right edges are spaces — horizontal tunnel wrap uses `col % COLS`.
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><b>Pac-Man movement & cornering</b></summary>
|
||||
|
||||
Both `pac_dir` (current) and `next_dir` (buffered) are kept. On each 0.14 s tick the code tries `next_dir` first; if the cell ahead is a wall or door it falls back to `pac_dir`. The mouth glyph alternates between the directional arrow and `'O'` every tick.
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><b>Ghost AI</b></summary>
|
||||
|
||||
Each ghost picks the non-reverse, walkable neighbour with the smallest squared-Euclidean distance to its target. Targets:
|
||||
- **Eaten** → home cell (returns to house, resets).
|
||||
- **Frightened** → random cell (scatter).
|
||||
- **Normal** → Pac-Man position + per-ghost offset `[(0,0),(2,2),(-2,2),(2,-2)]`.
|
||||
|
||||
Directions are shuffled so ties break randomly. The door `'G'` is walkable for ghosts but not Pac-Man.
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><b>Frightened & eaten states</b></summary>
|
||||
|
||||
Eating a power pellet (`'o'`) sets `frightened=True` for 7 s on all non-eaten ghosts. During the last 2 s they blink white↔blue on the 0.35 s timer. Colliding with a frightened ghost awards 200 pts and marks it `eaten` (glyph `◌`, navigates home). Colliding with a normal ghost costs a life.
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><b>Rendering</b></summary>
|
||||
|
||||
The maze is centred via `offset_r / offset_c`. Every `addstr`/`addch` is wrapped in `try/except curses.error` so small terminals degrade gracefully. The HUD is green/bold one row below the maze. The splash screen shows a big block-letter title; the game-over screen reports win/lose and prompts Y/N.
|
||||
</details>
|
||||
|
||||
Save as `pacman.py` and run — enjoy! 🟡
|
||||
@@ -0,0 +1,26 @@
|
||||
---
|
||||
title: "2026-06-17-world_news"
|
||||
created: 2026-06-17
|
||||
updated: 2026-06-17
|
||||
type: summary
|
||||
tags: [newsletter, world_news]
|
||||
external: http://go/ui/posts/2779/
|
||||
---
|
||||
|
||||
# 世界重大新闻速览
|
||||
|
||||
- [印度教科书恢复“舞女”裸体雕像原图,此前遭涂黑引发争议](https://www.bbc.com/news/articles/c7vyzgl2142o)
|
||||
- [印度因考试泄题担忧临时封禁Telegram](https://www.bbc.com/news/articles/cn074j04l3eo)
|
||||
- [特朗普转向乌克兰问题:表态愿推动俄乌和谈](https://www.cnbc.com/2026/06/16/trump-zelenskyy-ukraine-war-iran-g7-summit.html)
|
||||
- [美国加州野火肆虐,已蔓延超2000英亩](https://www.bbc.com/news/videos/cn8q909jld2o)
|
||||
- [美国施压见效,古巴旅游业游客数暴跌58%](https://www.bbc.com/news/articles/ckg8zrm20jjo)
|
||||
- [SpaceX以600亿美元收购AI编程公司Cursor,助力IPO及AI竞争](https://www.cnbc.com/2026/06/16/spacex-spcx-cursor-acquisition-ipo.html)
|
||||
- [Spacex总裁肖特维尔被曝主导公司IPO进程](https://www.cnbc.com/2026/06/16/gwynne-shotwell-spacex-elon-musk-ipo.html)
|
||||
- [匈牙利议会通过法案,限制欧尔班未来八年内再任总理](https://www.bbc.com/news/articles/c621zplqrrno)
|
||||
- [索马里兰在以色列承认其独立后,于耶路撒冷开设使馆](https://www.bbc.com/news/articles/cj4gn4d54y0o)
|
||||
- [瑞典61岁男子因强迫妻子与120名男子发生性关系被判刑四年](https://www.bbc.com/news/articles/crlwxg721eeo)
|
||||
- [法国最年长女囚犯(79岁)因1995年肢解案出庭受审](https://www.bbc.com/news/articles/cy498llene1o)
|
||||
- [委内瑞拉与美国通用电气达成协议,重建国家电网](https://www.bbc.com/news/articles/c0jyzpv52yyo)
|
||||
- [伊朗官方宣扬对美谈判为胜利,民众却关注物价与战争恐惧](https://www.bbc.com/news/articles/cn4rwzxvl8ko)
|
||||
- [霍尔木兹海峡航运恢复面临三大障碍:安全、水雷与通行费](https://www.bbc.com/news/articles/cn4rw784nj2o)
|
||||
- [西雅图上空首次出现无人机组成的FIFA实时比分显示](https://www.bbc.com/news/videos/ced4y9ljye9o)
|
||||
Reference in New Issue
Block a user