sl-rust 1.0.17

SL - An ASCII art train animation
Documentation
# Flickering 問題分析


## 🔍 根本原因


### 問題の症状

- 画面が点滅する
- 特に行が描画される際に見える

### コード上の原因


**src/render.rs の draw_line() 関数**:
```rust
fn draw_line(terminal: &Terminal, start_x: i32, y: i32, line: &str) -> io::Result<()> {
    let y = y as u16;
    for (i, ch) in line.chars().enumerate() {
        let x = start_x + i as i32;
        if x >= 0 && x < terminal.width() as i32 {
            terminal.move_to(x as u16, y)?;  // ← execute!(MoveTo) 呼び出し
            terminal.write_str(&ch.to_string())?;  // ← write!() 呼び出し
        }
    }
    Ok(())
}
```

**src/terminal.rs の実装**:
```rust
pub fn move_to(&self, x: u16, y: u16) -> io::Result<()> {
    let mut stdout = io::stdout();
    execute!(stdout, MoveTo(x, y))?;  // ← 毎回新しい stdout インスタンス
    Ok(())
}

pub fn write_str(&self, s: &str) -> io::Result<()> {
    let mut stdout = io::stdout();
    write!(stdout, "{}", s)?;  // ← flush されない
    Ok(())
}
```

### Flickering メカニズム


**フレーム内での実行順序**:
```
1. clear() → execute!(Clear) + flush    # 画面が空白に
2. render_d51():
   - draw_line() で最初の行:
     * move_to() → execute!(MoveTo)
     * write_str() → write!() [flush されない]
     * move_to() → execute!(MoveTo)
     * write_str() → write!() [flush されない]
     ...
   - 複数の execute!() で部分的に画面が更新される
3. flush() → 最終的に flush

問題: Clear と初描画の間に「空白画面」が見える
     + 複数の execute!() で描画が分割される
     = チラつき効果
```

## ✅ 解決策


### 方針:Atomic 描画


**1つの execute!() で全コマンドを一括実行し、flush は最後に1回だけ**

```rust
render_frame() {
  // 1. 全コマンドを集めておく
  let commands = [
    Clear(ClearType::All),
    MoveTo(0, 10), Print("train_line_1"),
    MoveTo(0, 11), Print("train_line_2"),
    ...
  ];
  
  // 2. 1つの execute!() で一括実行
  execute!(stdout, commands)?;
  
  // 3. 最後に flush(これまで見えていなかった出力が確定)
  stdout.flush()?;
}
```

この方式だと:
- Clear と描画が atomic(分割されない)
- Flickering がない

## 📋 実装予定


### Step 1: Terminal API 再設計

- 複数の `move_to/write_str` 呼び出しの廃止
- 代わりに `render_atomic()` メソッド追加

### Step 2: Command ビルダーパターン

```rust
pub struct FrameBuilder {
  commands: Vec<...>,
}

impl FrameBuilder {
  fn add_text(&mut self, x, y, text) { ... }
  fn build(self) -> Vec<...> { self.commands }
}
```

### Step 3: Render ロジック改修

- `render_d51/c51/logo()` を文字列構築に変更
- MoveTo/Print コマンドをビルド

### Step 4: テスト

- Flickering 確認
- CPU/Memory チェック