diff --git a/.sync-state b/.sync-state index 7d2f0ad..07eb368 100644 --- a/.sync-state +++ b/.sync-state @@ -1 +1 @@ -2026-06-16T17:01:11.214068+10:00 \ No newline at end of file +2026-06-17T17:01:07.811986+10:00 \ No newline at end of file diff --git a/links-posts/2776-2026-06-17-technology.md b/links-posts/2776-2026-06-17-technology.md new file mode 100644 index 0000000..34d6959 --- /dev/null +++ b/links-posts/2776-2026-06-17-technology.md @@ -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 正加速药物研发,但其对利润的具体提升程度尚待观察。 \ No newline at end of file diff --git a/links-posts/2777-2026-06-17-victoria.md b/links-posts/2777-2026-06-17-victoria.md new file mode 100644 index 0000000..f8f7703 --- /dev/null +++ b/links-posts/2777-2026-06-17-victoria.md @@ -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)在肖恩·埃文斯否认故意做出此手势后,将其澄清,埃文斯称那只是“不自主的抽搐”。 \ No newline at end of file diff --git a/links-posts/2778-glm-52-pacman.md b/links-posts/2778-glm-52-pacman.md new file mode 100644 index 0000000..888be1c --- /dev/null +++ b/links-posts/2778-glm-52-pacman.md @@ -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 + +
+Maze & cells + +`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`. +
+ +
+Pac-Man movement & cornering + +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. +
+ +
+Ghost AI + +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. +
+ +
+Frightened & eaten states + +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. +
+ +
+Rendering + +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. +
+ +Save as `pacman.py` and run — enjoy! 🟡 \ No newline at end of file diff --git a/links-posts/2779-2026-06-17-world_news.md b/links-posts/2779-2026-06-17-world_news.md new file mode 100644 index 0000000..0902811 --- /dev/null +++ b/links-posts/2779-2026-06-17-world_news.md @@ -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) \ No newline at end of file