目次
1.はじめに
2.概要
3.実装
4.実行結果
5.おわりに
1.はじめに
皆さんこんにちは。新人のtake123です。
今回、アドベントカレンダー参加にあたり、初めてQiitaの記事を執筆しています。
何を書くか悩みましたが、私自身まだ難しい事はまだよく分からないため、最近業務で使ったPowerShellスクリプトで、私と同じ初心者の人でも分かる簡単なプログラムを作成する事にしました。
という事で、今回はPowerShellスクリプトを使ったアナグラムゲームっぽいを作成してきます。
2.概要
冒頭でもお話した通り、今回は疑似アナグラムゲームを作成していきます。
疑似アナグラムゲームとは、文字を並び替えて意味のある言葉にするゲームです。
例を挙げるとこんな感じです。
例)問題:カツタンメンー
正解:ツタンカーメン
今回は、単語帳(テキストファイル)に単語を100個用意し、その単語帳の中からランダムで5つ単語を取り出し、各単語をランダムな並び順を文字列にして出題します。ユーザーは、出題された問題の並び順を正しくして回答します。間違えた場合は永遠と回答を繰り返します。
また、ゲーム性を持たせるため、出題されてから回答までにかかった時間を計測したいと思います。
フローチャートにするとこんな感じです。
3.実装
今回作成するゲームに必要な機能は以下の通りです。
1.テキストファイル読み込み
2.単語帳の中からランダムで単語を5つ取得
3.単語をシャッフル
4.問題を出題する
5.結果を出力する
では、実際に作っていきます。
3.1 テキストファイル読み込み
まず、テキストファイルを読み込んでいきます。
PowerShellでは、Get-Contentでテキストファイルを読み込みます。
$arrayFile = Get-Content -Encoding UTF8 ".\index.txt"
また、単語を取る際に文字化けしない様、文字コードはUTF-8に指定します。
3.2 単語帳の中からランダムで単語を5つ取得
ここではGet-Randomを使い、0~99からランダムな数字を5つ受け取ります。その後、先程読み込んだ単語帳から受け取った数字がインデックス番号の単語を取得します。
$arrays = @()
$arrays = 0..99 | Get-Random -Count 5 | % {$arrayFile[$_]}
ここで使っている % ですが、実際にはForEach-Objectと同様です。aliasを使ってなかった方は、PowerShell上でaliasと入力すると色々なaliasが出てきますので、是非見て見てみてください。
3.3 単語をシャッフル
単語をシャッフルするという事で、以下3つのステップで行います。
1.単語を1文字ずつ区切って配列に入れる
2.配列をシャッフル
3.配列を文字列に戻し、出題問題の配列に格納
文字での説明ではイメージしづらいと思いますので、実際のコードで書いてみるとこんな感じです。
$questions = @()
foreach($array in $arrays){
#文字列を配列に変換
$arr = $array.ToCharArray()
#配列をシャッフル
$arrShuffle = Get-Random $arr -Count $arr.Length
#配列を文字列に戻し、出題問題の配列に格納
$arrShuffle -join "," | % {$_.Replace(",","")} | ? {$_ -ne $array} | % {$questions += $_;break}
}
ToCharArray()でStringをCharにし、配列に格納します。
その後、Get-Randomを使い、配列をランダムに入れ替えます。
最後に、配列を-Joinでくっつけて、配列を区切る,をReplaceで空文字に変えます。
今回はこの処理で、単語をシャッフルする事にしました。
3.4問題を出題する
まずは、問題を出して、回答してもらうまでの処理を作成します。
今回作るゲームは、正解するまで無限に回答できるようにしたいので、入力処理を無限ループの中に入れる事にします。
また、この処理を問題の数だけ繰り返します。
コードに示すと以下の様になります。
$times = @()
for($i = 0;$i -lt $questions.Length;$i++){
Write-Host -NoNewline "第" ($i+1) "問`n" $questions[$i] " > "
#正解するまで無限に続く
while(1){
Read-Host | ? {$_ -eq $arrays[$i]} | % {break}
Write-Host -NoNewline "残念ながら違います。よく考えてみてください。`n" $questions[$i] " > "
}
}
次に、回答までにかかった時間を取得する処理を作成します。
今回は、操作の実行時間を計測してくれるMeasure-Commandを使っていきます。使い方は簡単で、Measure-Commandの中に実行時間を計測したい処理を入れるだけです。今回は、正解するまでの時間を計測したいので、以下の様に書いてみました。
$times = @()
for($i = 0;$i -lt $questions.Length;$i++){
Write-Host -NoNewline "第" ($i+1) "問`n" $questions[$i] " > "
#回答までにかかった時間を配列に格納
$times += (Measure-Command{
while(1){
#正解するまで無限に続く
Read-Host | ? {$_ -eq $arrays[$i]} | % {break}
Write-Host -NoNewline "残念ながら違います。よく考えてみてください。`n" $questions[$i] " > "
}
}).TotalSeconds
}
Measure-Commandの後ろについている.TotalSecondsですが、こちらを指定する事で、実行時間を秒単位で返してもらう事ができます。
3.5 結果を出力する
ついに実装も終わりに近づいてきました。
結果を出力していきます。
今回出力する結果は、
問題:〇〇 正解:〇〇 タイム:〇秒
問題:〇〇 正解:〇〇 タイム:〇秒
問題:〇〇 正解:〇〇 タイム:〇秒
問題:〇〇 正解:〇〇 タイム:〇秒
問題:〇〇 正解:〇〇 タイム:〇秒
合計タイムは...〇秒でした!
って感じにしようと思っています。
その為、まずは計測タイムから合計タイムを算出しながら各問題ごとの結果を出力する処理を作成します。
$total = 0
#計測タイムから合計タイムを算出
for($i = 0;$i -lt $questions.Length;$i++){
[Math]::Round($times[$i], 2) | % {Write-Host "第"($i+1)"問 問題:" $questions[$i] "`t答え:" $arrays[$i] "`tタイム:" $_ "秒";$total += $_}
}
結果として出力するタイムは、小数第二位まで表示するようにしてみました。
最後に、合計のタイムを出して終わりです。
$total = 0
#計測タイムから合計タイムを算出
for($i = 0;$i -lt $questions.Length;$i++){
[Math]::Round($times[$i], 2) | % {Write-Host "第"($i+1)"問 問題:" $questions[$i] "`t答え:" $arrays[$i] "`tタイム:" $_ "秒";$total += $_}
}
#合計タイムを出力
write-host "合計タイムは..." $total "秒でした!"
これで一通り終わりました!
最後に、諸々微調整をして最終的に出来上がったコードがこちらになります。
[Console]::OutputEncoding = [Text.Encoding]::UTF8
$questions = @()
$arrays = @()
$times = @()
$total = 0
$arraytxt = Get-Content -Encoding UTF8 ".\index.txt"
Write-Host -NoNewline "エンターを押して開始!!!"
Read-Host | % {clear}
#問題をランダムで取得
$arrays = 0..99| Get-Random -Count 5 | % {$arraytxt[$_]}
#シャッフル
foreach($array in $arrays){
while(1){
#文字列を配列に変換
$arr = $array.ToCharArray()
#配列をシャッフル
$arrShuffle = Get-Random $arr -Count $arr.Length
#配列を文字列に戻し、出題問題の配列に格納
$arrShuffle -join "," | % {$_.Replace(",","")} | ? {$_ -ne $array} | % {$questions += $_;break}
}
}
#出題
for($i = 0;$i -lt $questions.Length;$i++){
clear
Write-Host -NoNewline "第" ($i+1) "問`n" $questions[$i] " > "
#回答までにかかった時間を配列に格納
$times += (Measure-Command{
while(1){
#正解するまで無限に続く
Read-Host | ? {$_ -eq $arrays[$i]} | % {break}
Write-Host -NoNewline "残念ながら違います。よく考えてみてください。`n" $questions[$i] " > "
}
}).TotalSeconds
}
clear
#計測タイムから合計タイムを算出
for($i = 0;$i -lt $questions.Length;$i++){
[Math]::Round($times[$i], 2) | % {Write-Host "第"($i+1)"問 問題:" $questions[$i] "`t答え:" $arrays[$i] "`tタイム:" $_ "秒";$total += $_}
}
#合計タイムを出力
write-host "合計タイムは..." $total "秒でした!"
※上記をファイル保存する場合は、文字コード:UTF-8のBOM付きで作成してください。
では次に、実際に完成したゲームを動かしてみましょう。
4.実行結果
ファイルを実行すると、スタート画面が出てきます。
では、エンターを押して次に進みます。
問題が出てきましたね。まずは間違えてみたいと思います。
間違えると、このようにメッセージが出力され、再度回答を促す事が確認できました。
正解すると、次の問題に遷移します。
では、正解を入力して、エンターボタンを押していきます。
次の問題に遷移しましたね。
では、とりあえず最後まで問題を解いて、結果を見てみます。
こんな感じになりました!
各問題のタイムも合計タイムも出ていて、いい感じですね!
5.おわりに
ここまで読んでくださリ、ありがとうございました!
私自身元々文章を書くのがあまり得意ではなく、説明で分かりづらい箇所があればご指摘ください。
今回作成したコードの中でもっと効率的に実装できる箇所がありましたら、コメント等で教えて頂ければと思います。
また、このゲームはまだ色々追加できる機能(タイムのランキング機能、難易度選択、コンソール上から問題追加等)があると思うので、興味のある方は是非続きを作ってみてください!