B-Teck!

お仕事からゲームまで幅広く

【Java】半角カナ判定

正規表現

  • U+FF65「・」(半角カナ中黒)~U+FF9F「゚」(半角半濁点)の範囲で判定する。

細かい各メソッドの挙動とかは下記を参照。
https://docs.oracle.com/javase/jp/8/docs/api/java/util/regex/Pattern.html
https://docs.oracle.com/javase/jp/8/docs/api/java/util/regex/Matcher.html

    /**
     * 半角カナチェック
     * 半角カナ以外を含む文字列の場合false
     * 
     * @param String value 判定対象文字列
     * @return boolean true:半角カナ、false:半角カナ以外
     */
    public static boolean isHankakuKana(String value) {
        return    java.util.regex.Pattern
                        .compile("^[\\uFF65-\\uFF9F]+$")
                        // "-"(半角ハイフン)をOKにしたい場合は下記
                        // .compile("^[\\uFF65-\\uFF9F\\s-]+$")
                        .matcher(value)
                        .matches();
    }
    
    //       java.util.regex.Pattern
    //       java.util.regex.Matcher
    //       の2つをimportした状態で下記の処理を行う場合と同等
    //
    //        Pattern pattern = Pattern.compile("^[\\uFF65-\\uFF9F\\s-]+$");
    //        Matcher matcher = pattern.matcher(value);
    //        return matcher.matches();

【JavaScript】半角の文字を判定する

正規表現

半角文字列の判定として「/[^\x01-\x7E]/」を使う。
[]の中に含まれる先頭のキャレット^は否定の意なので、
この場合はASCIIコードの「x01x7Eの範囲外の文字列」という意味になる。 これだけではカナは含まれないため、同様に「/[^\uFF65-\uFF9F]/」。
これはUTF-16の「半角中黒(FF65)~半濁点(FF9F)の範囲外の文字列」となる。

String.prototype.match

String.prototype.matchは文字列から正規表現にマッチングした文字を配列で返す関数で、
マッチングするものが無ければnullを返す。
value.match(/[\uFF65-\uFF9F]/gi)のようにgフラグ(グローバルフラグ)をつければ
マッチングした文字全てを配列として返すが、
デフォルトでは最初にマッチングした文字のみを返却する。

つまり、「全角の文字」が入力文字列に含まれていた場合は要素のある配列が返され、
!で評価を反転させることでfalseとなる。
逆に、「半角の文字」が含まれていた場合match()はnullを返すので、
!で評価を反転させることでtrueとなる。

function isHankaku(value){
    return !value.match(/[^\x01-\x7E]/) || !value.match(/[^\uFF65-\uFF9F]/);
}
function isHankakuKana(value){
    return !value.match(/[^\uFF65-\uFF9F]/);
}

console.log("---------------------------------------------");
console.log("isHankaku     : ");
console.log("あ            : " + isHankaku("あ"));
console.log("-             : " + isHankaku("-"));
console.log("ア             : " + isHankaku("ア"));
console.log("A             : " + isHankaku("A"));
console.log("あいうえオ     : " + isHankaku("あいうえオ"));
console.log("アイウエオ         : " + isHankaku("アイウエオ"));
console.log("---------------------------------------------");
console.log("isHankakuKana : ");
console.log("あ            : " + isHankakuKana("あ"));
console.log("-             : " + isHankakuKana("-"));
console.log("ア             : " + isHankakuKana("ア"));
console.log("A             : " + isHankakuKana("A"));
console.log("あいうえオ     : " + isHankakuKana("あいうえオ"));
console.log("アイウエオ         : " + isHankakuKana("アイウエオ"));
console.log("---------------------------------------------");

// 結果
//---------------------------------------------  
//isHankaku     :   
//あ            : false  
//-             : true  
//ア             : true  
//A             : true  
//あいうえオ     : false  
//アイウエオ         : true  
//---------------------------------------------  
//isHankakuKana :   
//あ            : false  
//-             : false  
//ア             : true  
//A             : false  
//あいうえオ     : false  
//アイウエオ         : true  
//---------------------------------------------  

【Java】文字列の空白埋め、0埋め

String.formatで桁数を揃えたあとreplaceで空白を任意の文字に置き換えることで、0埋めができる。

   public static void main (String[] args) throws java.lang.Exception {
    
        System.out.println(zeroPadding("12345",10));    // 0000012345
        System.out.println(blankPadding("t e s t",10)); // t e s t
        
        // 先頭の文字列以外の空白も置換してしまうので、
        // 0埋めは空白を含む文字列には使えない
        System.out.println(zeroPadding("t e s t",10));  // 000t0e0s0t
    }
    
    /**
    *  lengthで指定した桁数まで空白埋めした文字列を返却する
    *   @param  input  入力文字列
    *   @param  length 空白埋めする桁数
    *   @return   length分まで空文字を付加したinput
    */
    public static String blankPadding(String input, int length){
        return String.format("%" + length + "s", input);
    }
    /**
    *  lengthで指定した桁数までゼロ埋めした文字列を返却する
    *   @param  input  入力文字列
    *   @param  length ゼロ埋めする桁数
    *   @return   length分まで0を付加したinput
    */
    public static String zeroPadding(String input, int length){
        return String.format("%" + length + "s", input).replace(" ", "0");
    }

もし先頭以外に空白が入りうる文字列を0埋めする場合、
先に必要な文字数分作ってからつなげて返すのが良さそう。

   /**
    *  lengthで指定した桁数までゼロ埋めした文字列を返却する
    *  与えられた文字列よりlengthが短い場合、そのまま返却する
    *   @param  input  入力文字列
    *   @param  length ゼロ埋めする桁数
    *   @return   length分まで0を付加したinput
    */
    public static String zeroPadding(String input, int length){
        int l = length - input.length();
        if (l > 0){
            String pad = String.format("%" + l + "s", "")
                               .replace(" ", "0");
            return pad + input;
        }else{
            return input;
        }
    }

行頭に付加した0を除去する場合は下記で

    public static String zeroSuppress(String input){
        return input.replaceAll("^0{1,}", "");
    }

【JavaScript】JSの配列操作まとめ

配列の操作

配列の作成

// 空の配列を作成
var hoge = [];                
// 値をもった配列を作成
var hoge = [1, 2, 3];      
// 長さ10で値が未定義の配列を作成
var hoge = new Array(10); 

配列の初期化

配列の変数に空の配列を代入するか、長さを0にすることで配列自体の初期化が行える。

// 空の配列を代入する場合
var hoge = [1, 2, 3];
hoge = [];
console.log(hoge);

// 配列の長さを0にする場合
hoge = [1, 2, 3];
hoge.length = 0;
console.log(hoge);

ちなみに、配列の長さを0ではない数に変更した場合は下記のようになる。

// 配列の長さを短くする場合
// 指定した要素数を残して削除される
var hoge = [1, 2, 3];
hoge.length = 1;
console.log(hoge);


// 配列の長さを長くする場合
// 未定義の要素が生成される
hoge = [1, 2, 3];
hoge.length = 5;
console.log(hoge);

配列の要素数を取得

var hoge = [1, 2, 3];
// 3と出力される
console.log(hoge.length);

配列の最初と最後を取得

var hoge = [1, 2, 3];
// 最初
console.log(hoge[0]);
// 最後
// 配列の長さは要素の個数だけど添字は0から始まるので
// 要素数から1を引くと最後の要素の添字になる
console.log(hoge[hoge.length - 1]);  

配列の要素を指定位置で切り出す

var hoge = [1, 2, 3];
// sliceは2個目の引数の添字を含まずに切り出す
// 下記のような場合は0番目の要素から添字が1のものまで
console.log(hoge.slice(0, 2));
// 下記のような場合は1番目の要素から添字が2のものまで
console.log(hoge.slice(1, 3));

配列のループ

for of または foreachを使うのがベター?

  • for
var fruits = ["りんご", "バナナ"];
for(let i = 0; i < fruits.length; i++) {
    console.log(fruits[i])
}
  • foreach
var fruits = ["りんご", "バナナ"];
fruits.forEach(function (item, index, array) {
    console.log(item, index);
});
  • for in
    for inはprototypeで設定されたプロパティも参照してしまうので、hasOwnPropertyを使わないと予期しないデータが混ざる場合がある。
var fruits = ["りんご", "バナナ"];
for (var key in fruits) {
    if (fruits.hasOwnProperty(key)){
        console.log(key + ' is property of fruits!');
    }
}
  • for of
var fruits = ["りんご", "バナナ"];
// 値を取り出す
for(let v of fruits) {
    console.log(v);
}
// キーを取り出す
for(let k of Object.keys(fruits)) {
    console.log(k);
}
// キーと値を取り出す
for(let k of Object.keys(fruits)) {
    console.log(k);
    console.log(fruits[k]);
}

配列の先頭に要素を追加する

var fruits = ["りんご", "バナナ"];
fruits.unshift("いちご");
console.log(fruits); 

配列の末尾に要素を追加する

var fruits = ["りんご", "バナナ"];
fruits.push("みかん");
console.log(fruits); 

配列の値を削除する

delete演算子は、削除したインデックスを詰めないので注意。

var fruits = ["いちご","りんご", "バナナ", "みかん"];
delete fruits[2];
// Array [ "いちご", "りんご", <1 empty slot>, "みかん" ]のような形になる
console.log(fruits);

配列の先頭の要素を取り出して元の配列から削除する

var fruits = ["りんご", "バナナ"];
// shiftの戻り値は削除した値なので、"りんご"が出力される
console.log(fruits.shift());
console.log(fruits);

配列の末尾の要素を取り出して元の配列から削除する

var fruits = ["りんご", "バナナ"];
// popの戻り値は削除した値なので、"バナナ"が出力される
console.log(fruits.pop());
console.log(fruits);

指定した要素に最初に合致した要素のインデックスを返す

var fruits = ["りんご", "バナナ", "りんご","りんご","バナナ"];
// 1と出力される
console.log(fruits.indexOf("バナナ"));

指定した要素に最後に合致した要素のインデックスを返す

var fruits = ["りんご", "バナナ", "りんご","りんご","バナナ"];
// 4と出力される
console.log(fruits.lastIndexOf("バナナ"));

インデックス位置を指定して要素を取り出し、元の配列から削除する

取り出した分のインデックスは詰められる

var fruits = ["いちご","りんご", "バナナ", "みかん"];
// 添字0から3個取り出す
// Array [ "いちご", "りんご", "バナナ" ]
console.log(fruits.splice(0, 3));
// Array [ "みかん" ]
console.log(fruits);


fruits = ["いちご","りんご", "バナナ", "みかん"];
// 添字1から2個取り出す
// Array [ "りんご", "バナナ" ]
console.log(fruits.splice(1, 2));
// Array ["いちご", "みかん"]
console.log(fruits);

インデックス位置を指定して要素を取り出し・削除し、新しい要素を追加する

上の処理の延長。
取り出す要素を指定したあと、引数に配列要素を入れることで元の配列を変更できる。

var fruits = ["いちご","りんご", "バナナ", "みかん"];
// 添字0から3個取り出して,取り出した部分に1個追加する
// Array [ "いちご", "りんご", "バナナ" ]
console.log(fruits.splice(0, 3,"ざくろ"));
// Array ["ざくろ" , "みかん"]
console.log(fruits);


fruits = ["いちご","りんご", "バナナ", "みかん"];
// 添字1から2個取り出して,取り出した部分に3個追加する
// Array [ "りんご", "バナナ" ]
console.log(fruits.splice(1, 2, "パイナップル", "マンゴー", "アセロラ"));
// Array [ "いちご", "パイナップル", "マンゴー", "アセロラ", "みかん" ]
console.log(fruits);

配列のコピー

fruits2 = fruitsのようにすると参照のコピーとなってしまうので、配列のコピーとならない。
コピーするときはArray.concat()Array.slice()を使う。

var fruits = ["いちご","りんご", "バナナ", "みかん"];
var fruits2 = fruits .concat();
var fruits3 = fruits.slice();
console.log(fruits);
console.log(fruits2);
console.log(fruits3);

配列の結合

破壊的な配列の結合

var fruits = ["いちご","りんご"];
var fruits2 = ["バナナ","みかん"];

Array.prototype.push.apply(fruits, fruits2);
// 一つ目の配列が直接変更される
console.log(fruits);
console.log(fruits2);

非破壊的な配列の結合

var fruits = ["いちご","りんご"];
var fruits2 = ["バナナ","みかん"];
var fruits3 = fruits.concat(fruits2);
// 元の配列を維持したまま、2つを結合した新しい配列が出来ている
console.log(fruits);
console.log(fruits2);
console.log(fruits3);

数値配列から最大値と最小値を取り出す

var array = [1234,15, 9999, 324, 78536];
// 最大値の取得
console.log(Math.max.apply(null, array));
// 最小値の取得
console.log(Math.min.apply(null, array));

配列に指定した値が含まれているかを確認する

var foods = ['りんご', 'バナナ', 'オレンジ'];
if (foods.includes('りんご')) {
    console.log("りんごは配列に含まれています。");
}
if (!foods.includes('キウイ')) {
    console.log("キウイは配列に含まれていません。");
}

配列のソート

//配列のソート
var a = [6,9,1,22,7,1,3,2,15,83];
//Array [ 6, 9, 1, 22, 7, 1, 3, 2, 15, 83 ]
console.log(a);
//昇順
a.sort(function(a,b){
    if( a < b ) return -1;
    if( a > b ) return 1;
    return 0;
});
//Array [ 1, 1, 2, 3, 6, 7, 9, 15, 22, 83 ]
console.log(a);
//降順
a.sort(function(a,b){
    if( a > b ) return -1;
    if( a < b ) return 1;
    return 0;
});
//Array [ 1, 1, 2, 3, 6, 7, 9, 15, 22, 83 ]
console.log(a);

参考 https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Array#Specifications

【ゲーム】バトルフィールド1で普段心がけていること

はじめに

f:id:beatdjam:20170523230103p:plain
昨年発売されたバトルフィールド1(PS4版)を未だにがっつりプレイしている。
基本オペ・コンクエ(たまにフロントライン)専門。
プレイ時間は100時間を超え、ランクも90台。そろそろ中堅を名乗っても良い頃だと思う。
今作は前作に比べて新規プレイヤーの数が多いという話を聞いた。
実際のところはどうだかは知らないけれど、もったいないなぁと思うプレイヤーをちょいちょい見る。
最適な行動かは別として、自分がゲーム中に心がけている行動をまとめてみた。

戦績は勝てば勝つほど良くなる

f:id:beatdjam:20170326030603j:plain
自身のスコアを少しでも伸ばすために、戦局にあまり影響の無い拠点を攻撃したり、
有利ポジションに居座って敵をキルし続けるプレイヤーをよく見かける。
そういうプレイヤーを否定するわけではないけど、
マッチ終了時に勝利チームへスコアボーナスが有ることを知ってほしい。
瞬間瞬間ではスコア的に美味しくない行動でも、
チームの勝利に貢献することでトータルではより高いスコアを得られる。
(とは言え、結局マッチングのバランスで左右されるので、
チーム移動をしなければ勝率は50%前後に落ち着く。)

兵科の強みを活かそう

各兵科は差別化されている。
敵の戦車が暴れて手をつけられないときに対戦車兵装を持った突撃兵でリスポーンしたり。
前線を抑えてデスを重ねていく味方に対して看護兵が足りない時に注射とパックで医者プレイをしたり。
長く膠着する拠点や巨大兵器へ攻撃を仕掛ける味方に弾を配る援護兵になったり。
味方が攻めあぐねている拠点をフレアスポットで援護して攻撃のきっかけを作る偵察兵になったり。
局面によって必要な兵科、ガジェット、武器は違う。
今使っている兵科がその時に最大限活かせる立ち回りを心がけよう。

マップや環境音に注意しよう

戦場で大事なのは情報だ。
マップの作りや銃声、足音…情報を得られる要素はいくらでもある。
マップを見れば地形や拠点、スポットされた敵に死んだ味方の位置が得られ、
次にどこに向かうべきか、どこを注意するべきかがわかる。
足音や銃声を聞いて、近くに味方のマークがなければそれは敵だ。
敵より先に敵の存在を知ること、敵が集中しているところを知ることは、
そのまま自分を優位に持っていくことにつながる。

オブジェクトに絡もう

f:id:beatdjam:20170523205651j:plain
電信施設の爆破や拠点の占領が必要なルールでは、
オブジェクトに絡むことが基本的に何よりも優先される行為だと思う。
自分自身が爆弾の設置や制圧、防衛を完了できない場合でも、
わずかでも自分に敵を引きつけることで味方の進軍経路や他拠点の優勢を作り出せる可能性がある。
特にオペレーションの攻撃側では、進軍側に旗に絡む人がどれだけいるかで勝敗が決まる。
旗を踏んでくれ!!

敵側拠点は負けてるとき以外基本的に不要

コンクエストは、個数的に敵味方対称となる形で拠点が配置されている。
つまり常に敵よりも多く拠点を保持し、確実に防衛することができれば勝利できるルールだ。
敵よりも多く拠点を確保していればしているほどゲージの蓄積は早くなるが、
よほどの戦力差が見られる場合以外は中央拠点より奥を攻めるべきではない。
過半数より多い拠点を保持しているということは、守るべき拠点が増えるということであり、
前線が流動的になるということでもある。
伸び切った前線はカウンターを容易にし、
敵拠点どころか本来守るべき中央拠点すら奪取される場合もある。

中央拠点から手前を制圧されて戦線の打破を目指している場合には、
裏取りで前線の兵士を剥がすことは戦略的な意味が十二分にある。
腕に自信があれば、負けているときには裏取りで前線の支援をしてみよう。

拠点制圧ルールでは中央拠点を先行して取る

前述のように、中央の拠点を先に取れるかで勝利の可能性が大きく変わってくる。
開幕でビークルや拠点の乗り物に乗れた場合は、手前の拠点から順番に攻めるのではなく、
中央の拠点制圧に向かうと序盤の有利を勝ち取れる可能性が高まる。
歩兵でも、なるべく走ってひとつふたつ先の拠点を目指して制圧することで、
より早く前線を上げることができる。

ビークルは有効に使おう

f:id:beatdjam:20170304022324j:plain
今作のビークルは、戦況を大きく左右する重要な要素だ。
拠点制圧に、前線の押し上げに、遊撃に、様々な状況で戦局を動かすことができる。
ただしそれは戦場の中で有効に活用してこそだ。
高台や後方でたまに主砲を撃っていたり、
目の前の制圧中の拠点を援護しに来なかったりするのはもってのほかだ。
芋っているビークルが高KDなのはなんの自慢にもならない。
戦闘で活躍することで自分の力を示そう。
※ 基本的に自走砲トラックを使うのは避けよう。リスポーン地点にもならず戦局に影響を与えづらい。

おわりに

ここで書いたことが全て正しいわけではないと思うけど、
それぞれが自分なりのプレイスタイルを考える切っ掛けにでもなればと思う。
戦場で会いましょう。

【VBA】与えられたパスのフォルダを新しく作成する

パスを指定してフォルダを作成する。

下記みたいな指定の場合でも存在しないパスごと作ってくれる。

    MakeNotExistsDir("存在するパスA\存在しないパスA")  
    MakeNotExistsDir("存在しないパスA\存在しないパスB\存在しないパスC")  

 

Option Explicit

'WindowsのAPIを使うので環境によって宣言を分ける
#If VBA7 And Win64 Then
    Declare PtrSafe Function SHCreateDirectoryEx Lib "shell32" Alias "SHCreateDirectoryExA" _
               (ByVal hwnd As LongPtr, ByVal pszPath As String, ByVal psa As LongPtr) As Long
#Else
    Declare Function SHCreateDirectoryEx Lib "shell32" Alias "SHCreateDirectoryExA" _
             (ByVal hwnd As Long, ByVal pszPath As String, ByVal psa As Long) As Long
#End If

'/**
' * MakeNotExistsDir
' * 与えられたパスのフォルダを新しく作る
' * @param DirPath              : 作成するフォルダのパス
' */
Sub MakeNotExistsDir(ByVal DirPath As String)
    Call SHCreateDirectoryEx(0&, DirPath, 0&)
End Sub

清く、正しく、残虐に!LET IT DIE

最近、PS4で配信された「LET IT DIE」をプレイしている。
海外では基本プレイ無料で配信されている本作だが、日本ではZ指定のため108円で配信されている。
結構好きなゲームなので紹介してみようと思う。

世界観

地殻変動によって東京に突如現れたバルブの塔を登り、頂上を目指すゲームだ。
塔の内部は、廃墟と化した地下鉄の駅やホーム等が広がっており、多数の敵が待ち構えている。
登場人物は少ないが、キャラは濃く、インパクトに残る事大。
良くも悪くもグラスホッパーのゲームらしいという感じ。

↓塔内には様々なロケーションが存在する f:id:beatdjam:20170224232236p:plain

システム

ゲームのプレイ感としては、動きの軽めなローグライクアクション。
公式ではハクスラという呼称も使っているが、
ジャンルとしてのハクスラを期待すると少し肩透かしかも。
道中で拾った設計図を拠点に持ち帰って、素材を集めて開発・強化して攻略に挑むのがメインとなる。

塔内の敵には普通の敵とは別に、他のプレイヤーが送り込んだ「ハンター」や、
プレイヤーの死体が襲い掛かってくる「ヘイター」のような種類がいる。
これはこのゲームの売りで、ダークソウルシリーズのような直接他プレイヤーと戦闘するわけではなく、
NPCとして現れるプレイヤーと戦う非同期型対戦だ。

また、協力プレイや直接対戦は存在しないが、敵の拠点に攻め入り、防衛を行っているNPCを倒したり、
破壊工作を行うことでポイントの争奪戦を行うチーム戦の要素もある。

塔内で死んだキャラクターは、先述の「ヘイター」となってしまい、自身は拠点に戻されてしまう。
その場合、ゲーム内通貨を支払うか、死亡した地点まで行って自分で倒なければ戻ってこない。
死亡にはある程度のリスクがあるといえる。

↓刃物や鈍器はもちろん、銃器もある。 f:id:beatdjam:20170224232019j:plain

難易度

序盤の階層は、アクションゲームがあまり得意ではなくてもなんとかなる難易度となっていると思う。
しかし、最初のボスを倒した後に地下鉄を抜け、地上に出られる11階からは少し様子が変わってくる。
隙の少ない武器や遠距離で大ダメージを叩き出すスナイパーライフルを装備した敵が現れるようになり、
不用意に飛び出すと囲まれるような地形が増え、雑魚の攻撃も威力が高くなっている。
ここからがLET IT DIEの本番といえるかもしれない。

↓11階の風景 長い地下鉄ステージを抜けた後なので空が見えるのは感慨深い f:id:beatdjam:20170224232238p:plain

課金要素

このゲームの課金要素は大きく分けて2つ。
デスメタルエクスプレスパスだ。
他にも、序盤のお助けアイテムなどのセットは販売されているが、ぶっちゃけいらない気がする。

  • デスメタル
    デスメタルとは、LET IT DIE内で用いられる課金通貨の名前だ。
    • 倉拡張庫
    • 死亡時にヘイター化しないでその場での復活
    • 武器開発や設備拡張にかかる時間の短縮

ができるが、まずは倉庫の拡張にデスメタルを使用するべき。
中盤以降はキャラクターの復活にも大きい代償がかかるようになってくるため、
コンティニューも場合によっては利用していいと思う。
よほど急いでいるのでなければ、時間短縮にはデスメタルを使うべきではない。
かなりもったいない。

  • エクスプレスパス
    エクスプレスパスとは、LET IT DIEを便利にすすめるための要素だ。
    一ヶ月単位で購入できる他、ログインボーナス等で1日有効チケットが配布されたりもする。
    効果としては、
    • 限定デカールの配布
    • 探索のお助け要素
    • 敵拠点の襲撃のお助け要素

ログインボーナスにデカール(このゲームのスキル)が配布されるようになったり、所持品の上限が増えたり、無料エレベーターが使えるようになったり。
襲撃時にも退却のタイミングが自由になったり、費用面で緩和が行われたりする。
でも無くても遊べちゃう。

↓奥がエクスプレス会員専用エレベーター。会員以外が近づくとドアが閉じられてしまう f:id:beatdjam:20170224232023j:plain

まとめ

とりあえず遊べるアクションゲームを探してる人、廃墟が好きな人、グラスホッパーが好きな人は、
PS4とクレカがあるならとりあえずやってみよう!
どうせ108円だ!
LET IT DIE!