0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

移動が可能かの判定・幅のある移動

Posted at

今回は paiza の「移動が可能かの判定・幅のある移動」の問題に挑戦!

これまでと違って、一マスだけの移動ではなく幅のある移動を扱う問題を解いていく!


🧩 問題概要

この問題は、マップ上で回転+直進を繰り返して移動するシミュレーション問題。

✔ マップ

  • 文字 '.' は通れる
  • '#' は障害物で通れない
  • 高さ H、幅 W

✔ 初期状態

  • 位置 (sy, sx)
  • 必ず 「北」向き からスタート

✔ 与えられる移動

N 回の移動が与えられる

  • d_i(L = 左回転、R = 右回転)
  • l_i(進むマス数)

移動手順は
① L or R で向きを変える
② その向きへ 1 マスずつ l_i 回進む


✔ 移動成功条件

  • マップ外に出ない
  • 進む経路に障害物が無い

上記がすべて満たされれば 移動後の座標を出力。


✔ 移動が失敗する場合

1マスずつ進む途中で

  • マップ外
  • 障害物 #
    に当たったらその瞬間の移動は中断し、

① 進めるところまで移動した位置を出力
"Stop" を出力
以降の移動はすべて無視して終了


入力:

H W sy sx N        
S_0         
...     
S_(H-1)     
d_1 l_1     
...     
d_N l_N



入力例:

10 10 6 4 3
..#.....#.
..........
##.#......
#.##....#.
.##.#.....
........#.
.#......#.
.#........
...#......
#.#.......
L 2
R 1
L 4

出力例:

6 2
5 2
5 0
Stop






✅ OK例:

const rl = require('readline').createInterface({ input: process.stdin });

const lines = [];

rl.on('line', input => lines.push(input));

rl.on('close', () => {
    const [rawH, rawW, sy, sx, rawN] = lines[0].split(' ');
    const H = Number(rawH);
    const W = Number(rawW);
    const N = Number(rawN);
    const gridS = lines.slice(1, H+1).map(line => line.split(''));
    const dl = lines.slice(H+1).map(line => line.split(' '));
    
    let y = Number(sy);
    let x = Number(sx);
    let dir = 'N';
    
    for (let i = 0; i < N; i++) {
        const d = dl[i][0];
        const l = Number(dl[i][1]);

        if (dir === 'N') {
            if (d === 'L') {
                dir = 'W';
            } else { // === 'R'
                dir = 'E';
            }
        } else if (dir === 'S') {
            if (d === 'L') {
                dir = 'E';
            } else { 
                dir = 'W';
            }
        } else if (dir === 'E') {
            if (d === 'L') {
                dir = 'N';
            } else { 
                dir = 'S';
            }
        } else { // === 'W'
            if (d === 'L') {
                dir = 'S';
            } else { 
                dir = 'N';
            }
        }
        
        for (let j = 1; j <= l; j++) {
            if (dir === 'N') y--;
            else if (dir === 'S') y++;
            else if (dir === 'E') x++;
            else x--;
          
            if (y < 0 || y >= H || x < 0 || x >= W || gridS[y][x] === '#') {
                if (dir === 'N') console.log(y+1, x);
                else if (dir === 'S') console.log(y-1, x);
                else if (dir === 'E') console.log(y, x-1);
                else console.log(y, x+1);
                
                console.log('Stop');
                return;
            }
        }
        
        console.log(y, x);
    }
});

🧭 コードの流れ

① 入力処理

  1. 1行目から
    • H(高さ)
    • W(幅)
    • sy(初期 y
    • sx(初期 x
    • N(移動回数)
      を読み取る
  2. 次の H 行を読み取り、
    gridS として「マップ」を 2 次元配列で保持する
    . = 通れる、# = 障害物)
  3. 残りの N 行を
    d_i(向き L/R)と l_i(進むマス数)として保存

② 初期状態のセット

  1. 現在位置を y = sy, x = sx に設定
  2. 最初は必ず北向き → dir = 'N'

N 回の移動を順番に処理

  1. d_i(L/R)が与えられたら、
    現在の dir(N/S/E/W)に応じて新しい向きに変更
    (左旋回 or 右旋回)

④ 1 マスずつ進む(最大 l_i 回)

  1. その方向へ 1 マス移動する
    • N → y--
    • S → y++
    • E → x++
    • W → x--
  2. 移動後のマスが以下のどちらかなら「移動失敗」
    • マップ外に出た
    • gridS[y][x]#(障害物)

⑤ 失敗したときの処理

  1. 直前の正常位置に戻して座標を出力
  2. "Stop" を出力して 全処理を終了

⑥ 成功したときの処理

  1. 最後まで移動できた場合、移動後の (y, x) を出力
  2. 次の移動へ進む



⭐(最終的な動作)

  • 全移動を正しく終えたら N 行分の座標 が出力される
  • 途中で障害物や外に出たら
  • 1 回だけ停止座標 + “Stop” が出力され、残りの移動は無視される






✨ 辞書化(オブジェクト)で短縮コード

const rl = require('readline').createInterface({ input: process.stdin });
const lines = [];
rl.on('line', l => lines.push(l));
rl.on('close', () => {
    const [H, W, sy, sx, N] = lines[0].split(' ').map(Number);
    const grid = lines.slice(1, 1 + H).map(r => r.split(''));
    const moves = lines.slice(1 + H).map(l => l.split(' '));

    // 向きの回転辞書
    const turn = {
        N: { L: 'W', R: 'E' },
        S: { L: 'E', R: 'W' },
        E: { L: 'N', R: 'S' },
        W: { L: 'S', R: 'N' },
    };

    // 各方向ごとの進み量
    const dy = { N: -1, S: 1, E: 0, W: 0 };
    const dx = { N: 0, S: 0, E: 1, W: -1 };

    let y = sy, x = sx, dir = 'N';

    for (let i = 0; i < N; i++) {
        let [d, l] = moves[i];
        l = Number(l);

        // 左右回転
        dir = turn[dir][d];

        // l マス進む
        for (let step = 0; step < l; step++) {
            const ny = y + dy[dir];
            const nx = x + dx[dir];

            // 範囲外 or 障害物 → STOP
            if (ny < 0 || ny >= H || nx < 0 || nx >= W || grid[ny][nx] === '#') {
                console.log(y, x);
                console.log("Stop");
                return;
            }
            y = ny;
            x = nx;
        }

        console.log(y, x);
    }
});

🔥 特徴

  • 方向の回転を辞書(オブジェクト)で一発変換
  • 進行方向も辞書化
  • if 文ほぼゼロ(1つだけ)
  • 20 行以上の削減



✔ 方向回転は辞書で一発

turn[dir][d]
  • ブラケット記法によって動的に値を取得可能。
  • dird(L/R) の組み合わせで次の方向が取れる。

✔ 移動も辞書で一発

y += dy[dir];
x += dx[dir];

if 文が大幅に消える)

✔ 「前の位置に戻す」処理
y, x と 移動先を意味する ny, nx で区別することによって、移動不可かどうかの判定で不可だった場合でも、移動できるところまでの位置を残しておくことができる。






🗒️ まとめ


🔍 1. 方向の回転は辞書(オブジェクト)で一発変換できる
dir = turn[dir][d];

N → L → W
E → R → S
などを辞書化すると if 文が消える。


🔍 2. 進行方向の移動量も辞書化できる
y += dy[dir];
x += dx[dir];

これで方向ごとの処理を統合できる(if 不要)。


🔍 3. 「進む前に next(ny, nx)を計算」するのが重要

実際に yx を進める前に
→ 先のマスが範囲外/障害物かチェック

ダメなら現在位置 y,x を出力して Stop

これで「前の位置に戻す」コードが不要になる。


🔍 4. 移動は 1 マスずつ処理する必要がある

  • まとめて l 回進めてはダメ
  • 各ステップごとに障害物チェックが必須

🔍 5. if だらけのコードより辞書(オブジェクト)の方が正確で短い

  • 方向の組合せの取り違えが防げる
  • 可読性・保守性が非常に高まる
  • 記述量が 20 行以上減る




僕の失敗談(´;ω;`)と解決法🐈

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?