paizaラーニング レベルアップ問題集 Aランクレベルアップメニュー JavaScript へび

へび (paizaランク A 相当)

https://paiza.jp/works/mondai/a_rank_level_up_problems/a_rank_snake_mapmove_boss

JavaScriptで解いてみました。いくつか解答例を載せたので、理解しやすいところ、しっくりくるところを参考にしていただけたらと思います。

# 解答例
はじめに時刻tと右左dの配列を作っています。
```javascript:JavaScript
const fs = require("fs");
const input = fs.readFileSync("/dev/stdin", "utf-8").trim();
const lines = input.split("\n");

const [H,W,sy,sx,N] = lines[0].split(" ").map(Number);
//マップ
const S = lines.slice(1, H + 1).map(line => line.split(""));

const T = [];//時刻t
const D = [];//右左d
for (let i = 1; i <= N; i++) {
    const [t, d] = lines[i + H].split(" ");
    T.push(Number(t));
    D.push(d);
}

const direct = ['N', 'E', 'S', 'W'];
let now = 0;//directの添字

//スタート位置
let [y, x] = [sy, sx];
S[y][x] = "*";

let j = 0;//Tの添字
for (let time = 0; time < 100; time++) {
    //時刻tで方向転換
    if (time === T[j]) {
        if (D[j] === "L") {
            now = (now + 3) % 4;
        } else {
            now = (now + 1) % 4;
        }
        j++;//Tの次を準備
    }
    //移動
    if (direct[now] === 'N') {
        y -= 1;
    } else if (direct[now] === 'E') {
        x += 1;
    } else if (direct[now] === 'S') {
        y += 1;
    } else if (direct[now] === 'W') {
        x -= 1;
    } 
    
    //移動先が、マップ上かつ障害物がない,自分の体もない、なら進める
    if (0 <= y && y < H && 0 <= x && x < W && S[y][x] === ".") {
        S[y][x] = "*";
    } else {
        break;
    }
    
}
console.log(S.map(row => row.join("")).join("\n"));
```

# 解答例(C++の場合を参考)
移動量の関数moveを定義しています。
```javascript
const fs = require("fs");
const input = fs.readFileSync("/dev/stdin", "utf-8").trim();
const lines = input.split("\n");

const [H,W,sy,sx,N] = lines[0].split(" ").map(Number);

const S = lines.slice(1, H + 1).map(line => line.split(""));

const td = [];////時刻t右左d
for (let i = 1; i <= N; i++) {
    const [t, d] = lines[i + H].split(" ");
    td.push([Number(t), d]);
}
let mcount = 0;//td添字

const direct = ['N', 'E', 'S', 'W'];
let dcount = 0;//directの添字

const move = (D, M) => {//D方角NESW、M左右ストレート
    //M=Rのとき
    let LR = 1;
    add = 1;
    
    if (M === 'S') {//M=S:ストレート,方向転換なしの時、真っ直ぐ
        //移動量
        if (D === 'N') {
            y--;
        } else if (D === 'S') {
            y++;
        } else if (D === 'E') {
            x++;
        } else {
            x--;
        }
    } else { //方向転換あり
        //Lのとき
        if (M === "L") {
            LR = -1;
            add = 3;
        }
        //移動量
        if (D === 'N') {
            x += LR;
        } else if (D === 'S') {
            x -= LR;
        } else if (D === 'E') {
            y += LR;
        } else {
            x -= LR;
        }
        dcount += add;//方向転換 次のために
    }
};

//スタート位置
let [y, x] = [sy, sx];//const sy,sxなのでy,xに置換
S[y][x] = "*";

for (let time = 0; time < 100; time++) {

    //時刻tで方向転換
    if (time === td[mcount][0]) {//time=t
        //方向転換の時刻になったら左右についての move を呼び出し
        move(direct[dcount % 4], td[mcount][1]);//(D, M=m)
       
        mcount++;//次のtdを準備
    //方向転換なし    
    } else {
        move(direct[dcount % 4], 'S');//ストレートS,方向転換なし、真っ直ぐ
    }    
    
    //移動先が、マップ上かつ障害物がない,自分の体もない、なら進める
    if (0 <= y && y < H && 0 <= x && x < W && S[y][x] === ".") {
        S[y][x] = "*";
    } else {
        break;
    }
    
}
console.log(S.map(row => row.join("")).join("\n"));
```

# 解答例(Python3の場合を参考)
time_lrで時刻t右左dをまとめた配列を作っています。
```javascript:JavaScript
const fs = require("fs");
const input = fs.readFileSync("/dev/stdin", "utf-8").trim();
const lines = input.split("\n");

const [H,W,sy,sx,N] = lines[0].split(" ").map(Number);
const S = lines.slice(1, H + 1).map(line => line.split(""));

const time_lr = [];//時刻t右左d
for (let i = 1; i <= N; i++) {
    const [t, d] = lines[i + H].split(" ");
    time_lr.push([t, d]);
}
let time_index = 0;//time_lrの添字

const direct = ['N', 'E', 'S', 'W'];
let now = 0;//directの添字

//スタート位置
let [y, x] = [sy, sx];
S[y][x] = "*";


for (let time = 0; time < 100; time++) {
    //時刻tで方向転換
    if (time_index < N && time === time_lr[time_index][0]) {
        time_index += 1;//次を準備
        if (time_lr[time_index] === "L") {
            now = (now + 3) % 4;
        } else {
            now = (now + 1) % 4;
        }
        
    }
    //移動
    if (direct[now] === 'N') {
        y -= 1;
    } else if (direct[now] === 'E') {
        x += 1;
    } else if (direct[now] === 'S') {
        y += 1;
    } else if (direct[now] === 'W') {
        x -= 1;
    } 
    
    //移動先が、マップ上かつ障害物がない,自分の体もない、なら進める
    if (0 <= y && y < H && 0 <= x && x < W && S[y][x] === ".") {
        S[y][x] = "*";
    } else {
        break;
    }
    
}
console.log(S.map(row => row.join("")).join("\n"));
```
# 解答例(Ruby の場合参考)
方向の配列`move = [[-1, 0], [0, 1], [1, 0], [0, -1]]`を使っています。
```javascript:JavaScript
const fs = require("fs");
const input = fs.readFileSync("/dev/stdin", "utf-8").trim();
const lines = input.split("\n");

const [H,W,sy,sx,N] = lines[0].split(" ").map(Number);

const board = lines.slice(1, H + 1).map(line => line.split(""));

const turns = lines.slice(H + 1).map(line => line.split(" ").map((val, i) => 
    i === 0 ? Number(val) : val));//時刻t右左d

const move = [[-1, 0], [0, 1], [1, 0], [0, -1]];
let now = 0;//moveの添字

//スタート位置
let [y, x] = [sy, sx];//const sy,sxなのでy,xに置換
board[y][x] = "*";
let [t, d] = turns.shift();

for (let time = 0; time < 100; time++) {

    //時刻tで方向転換
    if (t === time) {//time=t
        if (d === 'L') {
            now += 3;
        } else {
            now += 1;
        }
       
        if (turns.length > 0) {
            [t, d] = turns.shift();
        }//次のturndを準備
    } 
    //移動
    ny = y + move[now % 4][0];
    nx = x + move[now % 4][1];
    
    //移動先が、マップ上かつ障害物がない,自分の体もない、なら進める
    if (!(0 <= y && y <= H - 1 && 0 <= x && x <= W - 1 && 
        board[ny][nx] !== "#" && board[ny][nx] !== "*")) {
        break;
    }
    [y, x] = [ny, nx];
    board[y][x] = '*';
}
console.log(board.map(row => row.join("")).join("\n"));
```


***

# 解答例(python3の場合を参考)2
はじめにtime_lrで時刻と右左をまとめた配列を作っています。
```javascript
const fs = require("fs");
const input = fs.readFileSync("/dev/stdin", "utf-8").trim();
const lines = input.split("\n");

let [H,W,sy,sx,N] = lines[0].split(" ").map(Number);
const s = lines.slice(1,H + 1).map((line) => line.split(""));

const time_lr = lines.slice(H + 1, H + N + 1).map(line => line.split(" "));
let time_index = 0;　//方向転換の回数

const directions = ["N","E","S","W"];
let now_direction = 0;

s[sy][sx] = '*';

for (let t_now = 0; t_now <= 99; t_now++) {
    //方向転換の時刻になったら
    if (time_index < N && String(t_now) === time_lr[time_index][0]) {
        const d = time_lr[time_index][1];
        time_index += 1;
        //方向転換
        if (d === "L") {
            now_direction = (now_direction + 3) % 4;
        } else {
            now_direction = (now_direction + 1) % 4;
        }
    
    }//if time_index
    
    // 1 マス身体を伸ばす。
    if (directions[now_direction] === "N") {
        sy -= 1;
    } else if (directions[now_direction] === "E") {
        sx += 1;
    } else if (directions[now_direction] === "S") {
        sy += 1;
    } else if (directions[now_direction] === "W") {
        sx -= 1;  
    } 
    
    //移動できるようなら s[sy][sx] = "*" とマスを書き換え、
    //できないようならループを抜けます。
    if (sx < 0 || W <= sx || sy < 0 || H <= sy || s[sy][sx] !== ".") {
        break;
    } else {
        s[sy][sx] = '*';
    }    
        
}//for t_now

//マップを出力
console.log(s.map(elm => elm.join("")).join("\n"));


```


# 解答例
蛇が行動100回するループの中で、方向転換をするかforループで調べています。
```javascript:JavaScript
const fs = require("fs");
const input = fs.readFileSync("/dev/stdin", "utf-8").trim();
const lines = input.split("\n");
//マップの行数 H と列数 W 
//現在の座標 sy , sx , 方向転換の回数 N
const [H,W,sy,sx,N] = lines[0].split(" ").map((v) => Number(v));

const map = lines.slice(1,H + 1).map((line) => line.split(""));

//現在の y, x 座標
let [y,x] = [sy,sx];
map[y][x] = '*';

//へびははじめ北を向いています。
const directions = ["N","E","S","W"];
let now_direction = 0; //向きを決めるインデックス

//時刻 0 から 99 までの間、へびは各時刻に次の行動を最大 100 回とります。
let stop = false; //途中で止まったか判定
for (let time = 0; time <= 99; time++) {
    //方向転換をおこなう時刻の場合、
    //指定の向きに方向転換したのち 1 マス身体を伸ばす。
    
    //方向転換を行う時刻か調べる
    for (let turn = 1; turn <= N; turn++) {
        
        //方向転換をおこなう時刻 t_i と方向転換の向き d_i 
        const t = Number(lines[H + turn].split(" ")[0]);
        const d =        lines[H + turn].split(" ")[1];
    
        //もし方向転換を行う時刻ならば
        if (time === t) {
            //方向転換をする
            if (d === "L") {
            now_direction = (now_direction + 3) % 4;
            break;
            } else {
            now_direction = (now_direction + 1) % 4;
            break;
            }
        }//if time=t
        
    } //for turn　方向転換完了
  
    // 1 マス身体を伸ばす。
    if (directions[now_direction] === "N") {
        y -= 1;
    } else if (directions[now_direction] === "E") {
        x += 1;
    } else if (directions[now_direction] === "S") {
        y += 1;
    } else if (directions[now_direction] === "W") {
        x -= 1;  
    } 
   
    //移動先がマップの範囲外
    if (x < 0 || W <= x || y < 0 || H <= y) {
        stop = true;
        break;
        
    //移動先が障害物か自分の身体   
    } else if (map[y][x] === "#" || map[y][x] === '*') {
        stop = true;
        break;
        
    //移動終了時にへびの体のあるマスを '*' に
    } else {
        map[y][x] = '*';
    }
    
　　//止まったらfor_timeループ抜ける
    if (stop) break;

} //for time

//マップを出力
console.log(map.map(elm => elm.join("")).join("\n"));
```