PowerShellで遊んでみた：ブロック崩しゲームの実装 (1)

## はじめに

こんにちは！今回は、ちょっと変わったことに挑戦してみました。「PowerShellで何か面白いことできないかな？」と思いついたのが、このプロジェクトの始まりです。そうです、PowerShellでブロック崩しゲームを作ってみたんです！

## なぜPowerShellでゲーム？

「えっ、PowerShellでゲーム？」と思った方も多いでしょう。実は私もそう思いました（笑）。普段はシステム管理やタスク自動化に使うPowerShellですが、たまには「遊び」の要素を取り入れてみるのも面白いんじゃないかと。

このプロジェクトの目的は以下の通りです：

1. PowerShellの可能性を探る
2. プログラミングを楽しむ
3. 意外性のあるものを作ってみる

実用性？いえいえ、今回はそんなことは考えていません。純粋に「やってみたら面白そうだから」という理由だけです。

## 実装の概要

さて、具体的にどうやってブロック崩しゲームを実装したのか、簡単に説明しましょう。

1. コンソール画面をゲームフィールドとして使用
2. 文字でパドル、ボール、ブロックを表現
3. 無限ループでゲームを進行
4. キー入力で操作する

```plantuml
@startuml
left to right direction
actor "プレイヤー" as player

rectangle "PowerShell ブロック崩しゲーム" {
  usecase "ゲームを開始" as UC1
  usecase "パドルを左に移動" as UC2
  usecase "パドルを右に移動" as UC3
  usecase "ボールを打ち返す" as UC4
  usecase "ブロックを破壊" as UC5
  usecase "スコアを獲得" as UC6
  usecase "ゲームオーバー" as UC7
}

player --> UC1
player --> UC2
player --> UC3
player --> UC4
UC4 ..> UC5 : <<include>>
UC5 ..> UC6 : <<include>>
UC4 ..> UC7 : <<extend>>

note right of UC2
  左矢印キーで
  パドルを左に移動
end note

note right of UC3
  右矢印キーで
  パドルを右に移動
end note

note bottom of UC7
  ボールを落とすと
  ゲームオーバー
end note
@enduml
```

使用したPowerShellの機能：
- `System.Console`クラス：画面描画とキー入力の検出
- 配列操作：ゲーム状態の管理
- ループと条件分岐：ゲームロジックの実装

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2648069/6c1a7416-4a0d-78ef-de96-10175111d5d8.png)

上の#ガブロック
◯がボール
====を矢印で操作してボールをうちかえす

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2648069/07702be0-df2e-428c-303a-d63da3147440.png)


## 面白かったポイント

1. **意外な表現力**：文字だけでもゲームらしく見える！
2. **シンプルな実装**：意外と少ないコード量で実現できた
3. **PowerShellの新しい一面**：普段と違う使い方で新鮮

## ソースコード

おまちかね、ソースコードです！

```powershell
# ゲームの設定
$width = 40
$height = 20
$paddleWidth = 6
$ballChar = "O"
$paddleChar = "="
$blockChar = "#"

# ゲームの状態
$paddleX = ($width / 2) - ($paddleWidth / 2)
$ballX = $width / 2
$ballY = $height - 2
$ballDX = 1
$ballDY = -1
$blocks = @()
$score = 0

# ブロックの初期化
for ($i = 0; $i -lt 5; $i++) {
    for ($j = 0; $j -lt $width; $j++) {
        $blocks += @{X = $j; Y = $i}
    }
}

# ゲームループ
while ($true) {
    # 画面クリア
    Clear-Host

    # ボールの移動
    $ballX += $ballDX
    $ballY += $ballDY

    # 壁との衝突判定
    if ($ballX -le 0 -or $ballX -ge $width - 1) {
        $ballDX *= -1
    }
    if ($ballY -le 0) {
        $ballDY *= -1
    }

    # パドルとの衝突判定
    if ($ballY -ge $height - 1) {
        if ($ballX -ge $paddleX -and $ballX -lt $paddleX + $paddleWidth) {
            $ballDY *= -1
        } else {
            break  # ゲームオーバー
        }
    }

    # ブロックとの衝突判定
    $newBlocks = @()
    foreach ($block in $blocks) {
        if ($ballX -eq $block.X -and $ballY -eq $block.Y) {
            $ballDY *= -1
            $score += 10
        } else {
            $newBlocks += $block
        }
    }
    $blocks = $newBlocks

    # 描画
    $screen = @()
    for ($y = 0; $y -lt $height; $y++) {
        $line = " " * $width
        if ($y -eq $ballY) {
            $line = $line.Remove($ballX, 1).Insert($ballX, $ballChar)
        }
        if ($y -eq $height - 1) {
            $line = $line.Remove($paddleX, $paddleWidth).Insert($paddleX, $paddleChar * $paddleWidth)
        }
        foreach ($block in $blocks) {
            if ($block.Y -eq $y) {
                $line = $line.Remove($block.X, 1).Insert($block.X, $blockChar)
            }
        }
        $screen += $line
    }

    # 画面表示
    $screen | ForEach-Object { Write-Host $_ }
    Write-Host "Score: $score"

    # キー入力
    if ([System.Console]::KeyAvailable) {
        $key = [System.Console]::ReadKey($true).Key
        if ($key -eq "LeftArrow" -and $paddleX -gt 0) {
            $paddleX--
        }
        if ($key -eq "RightArrow" -and $paddleX -lt $width - $paddleWidth) {
            $paddleX++
        }
    }

    # 速度調整
    Start-Sleep -Milliseconds 50
}

Write-Host "Game Over! Final Score: $score"
```

```plantuml
@startuml
actor プレイヤー
participant "ゲームループ" as GameLoop
participant "パドル" as Paddle
participant "ボール" as Ball
participant "ブロック" as Block
participant "スコア" as Score

プレイヤー -> GameLoop: ゲーム開始
activate GameLoop

loop ゲームが終了するまで
    プレイヤー -> Paddle: パドル移動（左右キー）
    activate Paddle
    Paddle -> GameLoop: パドル位置更新
    deactivate Paddle

    GameLoop -> Ball: 位置更新
    activate Ball
    Ball -> GameLoop: 新しい位置
    deactivate Ball

    GameLoop -> Ball: 衝突チェック（壁）
    activate Ball
    Ball -> GameLoop: 方向変更（必要に応じて）
    deactivate Ball

    GameLoop -> Ball: 衝突チェック（パドル）
    activate Ball
    Ball -> GameLoop: 方向変更（衝突時）
    deactivate Ball

    GameLoop -> Ball: 衝突チェック（ブロック）
    activate Ball
    Ball -> Block: 衝突？
    activate Block
    alt ブロックに衝突
        Block -> GameLoop: ブロック消去
        GameLoop -> Score: スコア加算
        activate Score
        Score -> GameLoop: 新しいスコア
        deactivate Score
    end
    Block -> Ball: 衝突結果
    deactivate Block
    Ball -> GameLoop: 方向変更（衝突時）
    deactivate Ball

    GameLoop -> GameLoop: 画面更新

    alt ボールが下端に到達
        GameLoop -> プレイヤー: ゲームオーバー
        break
    end
end

deactivate GameLoop
@enduml
```

## 遊び方

1. 上記のコードを`breakout.ps1`として保存
2. PowerShellを管理者権限で開く
3. おまじない（実行ポリシーの変更）：
   ```powershell
   Set-ExecutionPolicy RemoteSigned
   ```
4. ゲームスタート！：
   ```powershell
   .\breakout.ps1
   ```
5. 左右矢印キーでパドルを操作。ボールを落とさないように頑張ってください！

## 感想

正直、最初は「本当にできるのかな？」と不安でした。でも、実際にコードを書き始めると、どんどんアイデアが浮かんできて、気づいたら夢中になっていました。

PowerShellって、こんな使い方もできるんだ！という新鮮な驚きがありました。システム管理の強い味方が、こんな風に"遊び心"も持っていたなんて。

## まとめ

このプロジェクトを通じて、プログラミングの楽しさを再確認できました。「できるかな？」と思ったことにチャレンジすると、意外な発見があるものですね。

皆さんも、普段使っている言語やツールで「ちょっと変わったこと」にチャレンジしてみてはいかがでしょうか？きっと新しい発見があるはずです。

さあ、あなたなりの「遊び」を見つけてみましょう。それが、プログラミングの新しい楽しみ方になるかもしれません。

Happy coding and happy playing!