PowerShell Memo

このサイトはPowerShell(MSH/Monad)奮闘記です

管理人「newpops吉岡洋」が
「PowerShell(旧名:MSH/Monad)」の研究結果を日々綴っていきます。

【お知らせ】
この日記からPowerShellのTipsを抽出し「PowerShell FAQ」として整理しました。


2008-05-28

[][]オセロゲームを作ろう(7):盤面描画とカーソル移動(2)

盤面描画とカーソル移動

前回紹介した、盤面描画とカーソル移動のサンプルについて解説します。

盤面描画

135 : # オセロ盤の描画
136 : function Draw-Board()
137 : {
138 :     Write-Host "┌─┬─┬─┬─┬─┬─┬─┬─┐" -ForegroundColor $boardFgColor -BackgroundColor $boardBgColor
139 :     for($i=1; $i -le 7;$i++)
140 :     {
141 :         Write-Host "│ │ │ │ │ │ │ │ │" -ForegroundColor $boardFgColor -BackgroundColor $boardBgColor
142 :         Write-Host "├─┼─┼─┼─┼─┼─┼─┼─┤" -ForegroundColor $boardFgColor -BackgroundColor $boardBgColor
143 :     }
144 :     Write-Host "│ │ │ │ │ │ │ │ │" -ForegroundColor $boardFgColor -BackgroundColor $boardBgColor
145 :     Write-Host "└─┴─┴─┴─┴─┴─┴─┴─┘" -ForegroundColor $boardFgColor -BackgroundColor $boardBgColor
146 : }

Draw-Init関数で最初の3行のメッセージを、

そこからコールされるDraw-Board関数オセロ盤を描画します。

#139〜143行目はfor文を使っているので可読性が悪いですね。。。(^^;


結果はこんな感じ。

==================================
      Othello  by  PowerShell
==================================
┌─┬─┬─┬─┬─┬─┬─┬─┐
│ │ │ │ │ │ │ │ │
├─┼─┼─┼─┼─┼─┼─┼─┤
│ │ │ │ │ │ │ │ │
├─┼─┼─┼─┼─┼─┼─┼─┤
│ │ │ │ │ │ │ │ │
├─┼─┼─┼─┼─┼─┼─┼─┤
│ │ │ │ │ │ │ │ │
├─┼─┼─┼─┼─┼─┼─┼─┤
│ │ │ │ │ │ │ │ │
├─┼─┼─┼─┼─┼─┼─┼─┤
│ │ │ │ │ │ │ │ │
├─┼─┼─┼─┼─┼─┼─┼─┤
│ │ │ │ │ │ │ │ │
├─┼─┼─┼─┼─┼─┼─┼─┤
│ │ │ │ │ │ │ │ │
└─┴─┴─┴─┴─┴─┴─┴─┘


カーソル移動

152 : # 指定した位置にカーソルを移動する
153 : function Move-Cursor([int]$newX, [int]$newY)
154 : {
155 :     $coordinate = New-Object System.Management.Automation.Host.Coordinates $newX, $newY
156 :     $rui.CursorPosition = $coordinate
157 : }

Move-Cursor関数引数に指定した座標にカーソルを移動します。

引数は移動先のX座標、Y座標です。

座標を指定してCoordinatesオブジェクトを生成し、CursorPositionに代入すればOKです。


ちなみに、オセロ盤の左上のセルの座標は、X=2、Y=4(上から5行目、左から3byte目)です。

上下左右キー操作

 46 :         # 上下左右のキー操作
 47 :         if($keyCode -eq $keyMap::Up)   {Move-CursorByDirection ([String]$keyMap::Up);   continue}
 48 :         if($keyCode -eq $keyMap::Down) {Move-CursorByDirection ([String]$keyMap::Down); continue}
 49 :         if($keyCode -eq $keyMap::Left) {Move-CursorByDirection ([String]$keyMap::Left); continue}
 50 :         if($keyCode -eq $keyMap::Right){Move-CursorByDirection ([String]$keyMap::Right);continue}

キー入力の監視で解説した、Get-InputKey関数の中で上下左右キーを捕捉します。

上下左右キーのカーソル移動は処理が似ているので、Move-CursorByDirection関数に集約させており、

その際、引数として、Stringにキャストした$KeyMap(Keys列挙体)を渡しています。

159 : # 上下左右のキーを押した時のカーソル移動
160 : function Move-CursorByDirection([String]$key)
161 : {
162 :     ($oldX, $oldY) = ($rui.CursorPosition.X, $rui.CursorPosition.Y)
163 :     if($key -eq "Up")       {$newX = $oldX; if($oldY -gt 0){$newY = $oldY - 1}}
164 :     elseif($key -eq "Down") {$newX = $oldX; $newY = $oldY + 1}
165 :     elseif($key -eq "Left") {if($oldX -gt 0){$newX = $oldX - 1}; $newY = $oldY}
166 :     elseif($key -eq "Right"){$newX = $oldX + 1; $newY = $oldY}
167 :     Move-Cursor $newX $newY
168 : }

Move-CursorByDirection関数では、まず162行目で現在のカーソル位置を取得。

163〜166行目で引数(Up,Down,Left,Rightの4種類)に応じて、カーソルの移動先を計算しています。


[][]オセロゲームを作ろう(8):盤面描画とカーソル移動(3)


盤面描画とカーソル移動

引き続き、盤面描画とカーソル移動のサンプルについての解説です。


アプリでは、エラーやデバッグ情報など、オセロ盤の下方にメッセージを表示します。

メッセージを表示する際、一時的にカーソルをオセロ盤下方に移動しますが、その後、カーソルを元の位置に復帰しなければなりません。


そこで必要になる処理が、カーソル位置のバックアップリストアです。

それぞれ、Push-Cursor関数、Pop-Cursor関数が担当します。

カーソル位置のバックアップ

170 : function Push-Cursor()
171 : {
172 :     $script:savedCursorPositionX = $rui.CursorPosition.X
173 :     $script:savedCursorPositionY = $rui.CursorPosition.Y
174 : }

カーソル位置のリストア

176 : function Pop-Cursor()
177 : {
178 :     Move-Cursor $script:savedCursorPositionX $script:savedCursorPositionY
179 : }

2008-05-27

[][]オセロゲームを作ろう(6):盤面描画とカーソル移動(1)

盤面描画とカーソル移動

今回はオセロの盤面描画とカーソル移動です。

以下のサンプルを実行してみてください。


  1 : ###############################################################################
  2 : # 初期処理/定義
  3 : ###############################################################################
  4 : # アセンブリ読み込み/参照
  5 : $rui = $host.UI.RawUI
  6 : [void][Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms") 
  7 : $keyMap = [Windows.Forms.Keys]
  8 : # 初期化/定義
  9 : $boardStartLine = 3
 10 : ($startX, $startY) = (2, ($boardStartLine + 1))
 11 : ($msgStartLine, $debugMsgStartLine)     = (21, 23)
 12 : $script:screen = $null
 13 : #色定義
 14 : ($boardFgColor, $boardBgColor) = ("Black", "White")
 15 : 
 16 : ###############################################################################
 17 : # メイン
 18 : ###############################################################################
 19 : function Start-Game()
 20 : {
 21 :     Backup-ScrBuf
 22 :     Draw-Init
 23 :     Get-InputKey
 24 :     Restore-ScrBuf
 25 : }
 26 : 
 27 : ###############################################################################
 28 : # キー入力受付関連の関数
 29 : ###############################################################################
 30 : 
 31 : # キー入力関数
 32 : function Get-InputKey()
 33 : {
 34 :     while($true)
 35 :     {
 36 :         # キー取得
 37 :         $keyInfo = $rui.ReadKey("NoEcho,IncludeKeyDown")
 38 :         $keyCode = $keyInfo.VirtualKeyCode
 39 : 
 40 :         # Enterを入力
 41 :         if($keyCode -eq $keyMap::Enter){continue}
 42 : 
 43 :         # Escを入力→終了
 44 :         if($keyCode -eq $keyMap::Escape){return}
 45 : 
 46 :         # 上下左右のキー操作
 47 :         if($keyCode -eq $keyMap::Up)   {Move-CursorByDirection ([String]$keyMap::Up);   continue}
 48 :         if($keyCode -eq $keyMap::Down) {Move-CursorByDirection ([String]$keyMap::Down); continue}
 49 :         if($keyCode -eq $keyMap::Left) {Move-CursorByDirection ([String]$keyMap::Left); continue}
 50 :         if($keyCode -eq $keyMap::Right){Move-CursorByDirection ([String]$keyMap::Right);continue}
 51 : 
 52 :         # Aを入力→現在の座標を表示
 53 :         if($keyCode -eq $keyMap::A)
 54 :         {
 55 :             $dX = $rui.CursorPosition.X
 56 :             $dY = $rui.CursorPosition.Y
 57 :             $dMsg = "X : " + $dX + " Y : " + $dY
 58 :             Show-DebugMsg $dMsg
 59 :         }
 60 : 
 61 :     }
 62 : }
 63 : 
 64 : ###############################################################################
 65 : # メッセージ表示関数
 66 : ###############################################################################
 67 : 
 68 : # メッセージ表示
 69 : function Show-Msg($msg)
 70 : {
 71 :     Show-MsgInner $msgStartLine $msg $msgFgColor $msgBgColor
 72 : }
 73 : 
 74 : function Show-DebugMsg($msg)
 75 : {
 76 :     Show-MsgInner $debugMsgStartLine $msg $boardFgColor $boardBgColor
 77 : }
 78 : 
 79 : function Show-MsgInner([int]$pos, [String]$msg, [ConsoleColor]$fg, [ConsoleColor]$bg)
 80 : {
 81 :     Push-Cursor
 82 :     Move-Cursor 0 $pos
 83 :     Write-Host -NoNewLine "                                                                                    "
 84 :     Move-Cursor 0 $pos
 85 :     Write-Host -NoNewLine $msg -ForegroundColor $fg -BackgroundColor $bg
 86 :     Pop-Cursor
 87 : }
 88 : 
 89 : ###############################################################################
 90 : # スクリーン操作関数
 91 : ###############################################################################
 92 : 
 93 : # スクリーンバッファバックアップ
 94 : function Backup-ScrBuf()
 95 : {
 96 :     $rect = New-Object System.Management.Automation.Host.Rectangle
 97 :     $rect.Left   = 0
 98 :     $rect.Top    = 0
 99 :     $rect.Right  = $rui.WindowSize.Width
100 :     $rect.Bottom = $rui.CursorPosition.Y
101 :     $script:screen = $rui.GetBufferContents($rect)
102 : }
103 : 
104 : # スクリーンバッファリストア
105 : function Restore-ScrBuf()
106 : {
107 :     Clear-Host
108 :     $origin = New-Object System.Management.Automation.Host.Coordinates(0, 0)
109 :     $rui.SetBufferContents($origin, $script:screen)
110 :     $pos = New-Object System.Management.Automation.Host.Coordinates(0, $script:screen.GetUpperBound(0))
111 :     $rui.CursorPosition = $pos
112 : }
113 : 
114 : ###############################################################################
115 : # 描画関連の関数
116 : ###############################################################################
117 : 
118 : # 初期描画
119 : function Draw-Init
120 : {
121 :     # タイトルの描画
122 :     Clear-Host
123 :     Write-Host "=================================="
124 :     Write-Host "      Othello  by  PowerShell     "
125 :     Write-Host "=================================="
126 : 
127 :     # オセロ盤の描画
128 :     Move-Cursor 0 $boardStartLine
129 :     Draw-Board
130 : 
131 :     # カーソル位置の初期化
132 :     Move-Cursor $startX $startY
133 : }
134 : 
135 : # オセロ盤の描画
136 : function Draw-Board()
137 : {
138 :     Write-Host "┌─┬─┬─┬─┬─┬─┬─┬─┐" -ForegroundColor $boardFgColor -BackgroundColor $boardBgColor
139 :     for($i=1; $i -le 7;$i++)
140 :     {
141 :         Write-Host "│ │ │ │ │ │ │ │ │" -ForegroundColor $boardFgColor -BackgroundColor $boardBgColor
142 :         Write-Host "├─┼─┼─┼─┼─┼─┼─┼─┤" -ForegroundColor $boardFgColor -BackgroundColor $boardBgColor
143 :     }
144 :     Write-Host "│ │ │ │ │ │ │ │ │" -ForegroundColor $boardFgColor -BackgroundColor $boardBgColor
145 :     Write-Host "└─┴─┴─┴─┴─┴─┴─┴─┘" -ForegroundColor $boardFgColor -BackgroundColor $boardBgColor
146 : }
147 : 
148 : ###############################################################################
149 : # カーソル操作関数
150 : ###############################################################################
151 : 
152 : # 指定した位置にカーソルを移動する
153 : function Move-Cursor([int]$newX, [int]$newY)
154 : {
155 :     $coordinate = New-Object System.Management.Automation.Host.Coordinates $newX, $newY
156 :     $rui.CursorPosition = $coordinate
157 : }
158 : 
159 : # 上下左右のキーを押した時のカーソル移動
160 : function Move-CursorByDirection([String]$key)
161 : {
162 :     ($oldX, $oldY) = ($rui.CursorPosition.X, $rui.CursorPosition.Y)
163 :     if($key -eq "Up")       {$newX = $oldX; if($oldY -gt 0){$newY = $oldY - 1}}
164 :     elseif($key -eq "Down") {$newX = $oldX; $newY = $oldY + 1}
165 :     elseif($key -eq "Left") {if($oldX -gt 0){$newX = $oldX - 1}; $newY = $oldY}
166 :     elseif($key -eq "Right"){$newX = $oldX + 1; $newY = $oldY}
167 :     Move-Cursor $newX $newY
168 : }
169 : 
170 : function Push-Cursor()
171 : {
172 :     $script:savedCursorPositionX = $rui.CursorPosition.X
173 :     $script:savedCursorPositionY = $rui.CursorPosition.Y
174 : }
175 : 
176 : function Pop-Cursor()
177 : {
178 :     Move-Cursor $script:savedCursorPositionX $script:savedCursorPositionY
179 : }
180 : 
181 : # 処理スタート
182 : Start-Game

上記コードを実行すると、オセロの盤面を描画します。

実行直後

f:id:newpops:20080527014052j:image


上下左右キーを押下すると、キーの方向にカーソルが「1」移動します。

また、Aキーを押下すると現在のカーソル位置を表示します。

#カーソル位置を表示する処理はデバッグ目的で実装しています。

下に移動+カーソル位置表示

起動後、下キーを押下し、下方向に1移動。

その後、Aキーを押下し、カーソル位置を表示しました。

f:id:newpops:20080527014053j:image



ただ、このままではオセロ盤の枠上にもカーソルが移動してしまいます。

このオセロゲームを、

  • 上下左右キーでカーソル移動
  • Enterで駒を置く

という仕様にする場合、カーソルが枠上に移動可能なのは好ましくありません。

駒が配置可能な8×8のマス上にカーソル移動を制限する必要があります。


つづく。。。

2008-05-15

[][]オセロゲームを作ろう(5):スクリーンバッファ操作(3)

スクリーンバッファの操作

前回に引き続き、前々回紹介した、スクリーンバッファ操作のサンプルについて解説します。

スクリーンバッファリストア

# スクリーンバッファリストア
function Restore-ScrBuf()
{
    Clear-Host
    $origin = New-Object System.Management.Automation.Host.Coordinates(0, 0)
    $rui.SetBufferContents($origin, $script:screen)
    $pos = New-Object System.Management.Automation.Host.Coordinates(0, $script:screen.GetUpperBound(0))
    $rui.CursorPosition = $pos
}

Restore-ScrBuf関数は、Backup-ScrBuf関数バックアップしたスクリーンバッファリストアします。

手順は以下です。

  1. コンソールのクリア(Clear-Host)
  2. スクリーンバッファの描画座標の生成(Coordinatesオブジェクト
  3. スクリーンバッファの描画(SetBufferContentsメソッド)
  4. カーソル位置を描画領域の最終行に移動(CursorPositionプロパティ

まず、Clear-Host関数でコンソールをクリアします。

描画座標はコンソール左上ですので、X=0,Y=0のCoordinatesオブジェクトを生成します。

次に、SetBufferContentsメソッドでスクリーンバッファを描画します。

SetBufferContentsメソッド
System.Void SetBufferContents(Coordinates origin, BufferCell[,] contents)
System.Void SetBufferContents(Rectangle r, BufferCell fill)

SetBufferContentsメソッドは2種類ありますが、今回利用するのは前者で、

引数は、描画座標(Coordinatesオブジェクト)とスクリーンバッファ(BufferCellオブジェクトの2次元配列)です。


最後に、カーソル位置を描画領域の最終行に移動します。

最終行は、リストアするスクリーンバッファの1次元目の最終配列要素(最終行)になります。


つづく。。。

2008-05-14

[][]オセロゲームを作ろう(4):スクリーンバッファ操作(2)

スクリーンバッファの操作

前回紹介した、スクリーンバッファ操作のサンプルについて解説します。

スクリーンバッファバックアップ

# スクリーンバッファバックアップ
function Backup-ScrBuf()
{
    $rect = New-Object System.Management.Automation.Host.Rectangle
    $rect.Left   = 0
    $rect.Top    = 0
    $rect.Right  = $rui.WindowSize.Width
    $rect.Bottom = $rui.CursorPosition.Y
    $script:screen = $rui.GetBufferContents($rect)
}

PowerShellでコンソールの特定領域を取得するには、以下の処理を行います。

  1. 取得する領域を指定する。(Rectangleオブジェクト)
  2. コンソールの指定領域を取得する(GetBufferContentsメソッド)

まず、Rectangleオブジェクトを生成し、指定領域の座標をLeft,Top,Right,Bottomプロパティに設定します。

このサンプルでは、Rightプロパティに「コンソールWindowの横幅」、Bottomプロパティに「現在カーソル位置の行」を設定することで、全描画領域を指定しています。


次に、GetBufferContentsメソッドで領域を取得します。

GetBufferContentsメソッド
System.Management.Automation.Host.BufferCell[,] GetBufferContents(Rectangle r)

引数はRectangleオブジェクトで、戻り値BufferCellオブジェクトの2次元配列となっています。


サンプルでは、$script:screenに取得しており、内容は以下のように取得します。

$script:screen[0,5]  → 1行目の6桁目
$script:screen[1,10] → 2行目の11桁目

つづく。。。

2008-05-13

[][]オセロゲームを作ろう(3):スクリーンバッファ操作(1)

スクリーンバッファの操作

今回はスクリーンバッファの操作です。


具体的には、オセロゲームを実行するタイミングで、

その時点でコンソールに表示されている内容をバックアップし、

オセロが終了した際にバックアップ内容をコンソールにリストアします。


処理は、PowerShellコンテストのグランプリである「ウィンドウの切り替え」を参考にしています。

「キー入力監視」+「スクリーンバッファ操作」サンプル

# 初期処理
$rui = $host.UI.RawUI
[void][Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms") 
$keyMap = [Windows.Forms.Keys]
$script:screen = $null

# メイン
function Start-Game()
{
    Backup-ScrBuf
    Draw-Init
    Get-InputKey
    Restore-ScrBuf
}

# キー入力関数
function Get-InputKey()
{
    while($true)
    {
        # キー取得
        $keyInfo = $rui.ReadKey("NoEcho,IncludeKeyDown")
        $keyCode = $keyInfo.VirtualKeyCode

        # Enterを入力
        if($keyCode -eq $keyMap::Enter){Show-Msg 'Enter';continue}

        # Escを入力→終了
        if($keyCode -eq $keyMap::Escape){return}
    }
}

# メッセージ表示
function Show-Msg([String]$msg)
{
    Write-Host -NoNewLine $msg
}

# スクリーンバッファバックアップ
function Backup-ScrBuf()
{
    $rect = New-Object System.Management.Automation.Host.Rectangle
    $rect.Left   = 0
    $rect.Top    = 0
    $rect.Right  = $rui.WindowSize.Width
    $rect.Bottom = $rui.CursorPosition.Y
    $script:screen = $rui.GetBufferContents($rect)
}

# スクリーンバッファリストア
function Restore-ScrBuf()
{
    Clear-Host
    $origin = New-Object System.Management.Automation.Host.Coordinates(0, 0)
    $rui.SetBufferContents($origin, $script:screen)
    $pos = New-Object System.Management.Automation.Host.Coordinates(0, $script:screen.GetUpperBound(0))
    $rui.CursorPosition = $pos
}

# 初期描画
function Draw-Init
{
    Clear-Host
    Write-Host "=================================="
    Write-Host "      Othello  by  PowerShell     "
    Write-Host "=================================="
}

# 処理スタート
Start-Game


上記スクリプトを実行すると、コンソールの内容がバックアップ、クリアされ、以下の文字列が表示されます。

==================================
      Othello  by  PowerShell     
==================================

キー入力は前回と同様「Enter」と「Escape」だけ個別処理をしています。

Escapeキーを押すとアプリが終了し、先ほどバックアップしたコンソール内容をリストアします。



つづく・・・。