paizaラーニング レベルアップ問題集 Aランクレベルアップメニュー JavaScript いびつなリバーシ対戦

# いびつなリバーシ対戦 (paizaランク A 相当)

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

解いてみました。

## 解答コード例（JavaScript）
縦横斜めを一つ一つ書きました。
```javascript:javascript
const fs = require("fs");
const input = fs.readFileSync("/dev/stdin", "utf-8").trim();
const lines = input.split("\n");
//盤面の行数 H ,列数 W , プレイヤーの人数 N , 全プレイヤーの合計ターン数 n
const [H, W, N, n] = lines[0].split(" ").map(Number);
//盤面
const board = lines.slice(1, H + 1).map(line => line.split(""));
//石を置いていく
for (let turn = 1; turn <= n; turn++) {
    //i 回目の操作で石を置く人のプレイヤー番号 p_i と、その人が石を置く座標 Y_i X_i 
    const [p, Y, X] = lines[H + turn].split(" ").map(Number);
    // console.log(p,Y,X,"ーーーーーーーー");デバッグで使うと便利
    //盤面のマス(Y_i, X_i)に石を置く。既に相手の石が置かれている場合は相手の石を自分の石に置き換える。
    board[Y][X] = p;
    //次に、縦横斜めに自分の石ではさんだ連続した穴の空いていないマスに自分の石を置きます。
    //相手の石が置かれている場合は相手の石を自分の石に置き換えて、操作を終了する。
    
    //上方向を調べる。盤面内。
    for (let i = 1; Y - i >= 0; i++) {
        //穴があるならそこで終わり
        if (board[Y - i][X] === "#") {
            break;
        }
        //自分の石があったら、その間に自分の石を置く
        if (board[Y - i][X] === p) {
            for (let j = 1; j < i; j++) {
                board[Y - j][X] = p;
            }
            break;
        }
    }
    //下
    for (let i = 1; Y + i < H; i++) {
        //穴があるならそこで終わり
        if (board[Y + i][X] === "#") {
            break;
        }
        //自分の石があったら、その間に自分の石を置く
        if (board[Y + i][X] === p) {
            for (let j = 1; j < i; j++) {
                board[Y + j][X] = p;
            }
            break;
        }
    }
    
    //左
    for (let i = 1; X - i >= 0; i++) {
        //穴があるならそこで終わり
        if (board[Y][X - i] === "#") {
            break;
        }
        //自分の石があったら、その間に自分の石を置く
        if (board[Y][X - i] === p) {
            for (let j = 1; j < i; j++) {
                board[Y][X - j] = p;
            }
            break;
        }
    }
    
    //右
    for (let i = 1; X + i < W; i++) {
        //穴があるならそこで終わり
        if (board[Y][X + i] === "#") {
            break;
        }
        //自分の石があったら、その間に自分の石を置く
        if (board[Y][X + i] === p) {
            for (let j = 1; j < i; j++) {
                board[Y][X + j] = p;
            }
            break;
        }
    }    
    //右上
    for (let i = 1; Y - i >= 0 && X + i < W; i++) {
        //穴だったら終わり
        if (board[Y - i][X + i] === "#") {
            break;
        }
         //自分の石があったら、その間に自分の石を置く
        if (board[Y - i][X + i] === p) {
            for (let j = 1; j < i; j++) {
                board[Y - j][X + j] = p;
            }
            break;
        }
    }
     //右下
    for (let i = 1; Y + i < H && X + i < W; i++) {
        //穴だったら終わり
        if (board[Y + i][X + i] === "#") {
            break;
        }
         //自分の石があったら、その間に自分の石を置く
        if (board[Y + i][X + i] === p) {
            for (let j = 1; j < i; j++) {
                board[Y + j][X + j] = p;
            }
            break;
        }
    }
    //左下
    for (let i = 1; Y + i < H && X - i >= 0; i++) {
        //穴だったら終わり
        if (board[Y + i][X - i] === "#") {
            break;
        }
         //自分の石があったら、その間に自分の石を置く
        if (board[Y + i][X - i] === p) {
            for (let j = 1; j < i; j++) {
                board[Y + j][X - j] = p;
            }
            break;
        }
    }
    //左上
    for (let i = 1; Y - i >= 0 && X - i >= 0; i++) {
        //穴だったら終わり
        if (board[Y - i][X - i] === "#") {
            break;
        }
         //自分の石があったら、その間に自分の石を置く
        if (board[Y - i][X - i] === p) {
            for (let j = 1; j < i; j++) {
                board[Y - j][X - j] = p;
            }
            break;
        }
    }
    // console.log(board.map(row => row.join("")).join("\n"));デバッグで使うと便利
} //for turn

//盤面を出力
console.log(board.map(row => row.join("")).join("\n"));
```

## 解答コード例（JavaScript）C++の場合の模範解答参考
上下、左右、左上右下、左下右上をまとめているところが特徴です。
```javascript:javascript
const fs = require("fs");
const input = fs.readFileSync("/dev/stdin", "utf-8").trim();
const lines = input.split("\n");
//盤面の行数 H ,列数 W , プレイヤーの人数 N , 全プレイヤーの合計ターン数 n
const [H, W, N, n] = lines[0].split(" ").map(Number);
//盤面
const S = Array(H);
for (let i = 0; i < H; i++) {
    S[i] = lines[i + 1].split("");
}

for (let h = 0; h < n; h++) {
    //石を置く人のプレイヤー番号 P_i と、その人が石を置く座標 Y_i X_i
    const [P, Y, X] = lines[H + 1 + h].split(" ").map(Number);
    //上下
    for (let i = -1; i <= 1; i += 2) { //iで上(i=-1)か下(i=1)か決める
        //jでYの隣から順番に調べていく
        for (let j = 1; ; j++) {
            //盤面から出た　または　穴だった場合
                //(i*j)がYから進んだマス数
            if(Y+(i*j) == -1 || Y+(i*j) == H || S[Y+(i*j)][X] == '#'){ 
                    break;//そこで終わり
            }
            //自分の石だったら
            if(S[Y+(i*j)][X] == P){
                //はさんだマスに石を置いていく
                    //上下まとめているので、Math.minとMath.maxで
                    //石があったところY+(i*j)から石を置いたところYまで、と
                    //石を置いたところYから石があったところY+(i*j)まで、を場合分ける
                for (let k = Math.min(Y+(i*j),Y); k <= Math.max(Y+(i*j),Y); k++){
                    S[k][X] = P;
                }
                break;
            }
        }
    }
    //左右
    for (let i = -1; i <= 1; i+=2){
        for (let j = 1; ; j++){

            if(X+(i*j) == -1 || X+(i*j) == W || S[Y][X+(i*j)] == '#'){
                break;
            }

            if(S[Y][X+(i*j)] == P){
                for (let k = Math.min(X+i*j,X); k <= Math.max(X+i*j,X); k++){
                    S[Y][k] = P
                }
                break;
            }
        }
    }
    //左上、右下
    for (let i = -1; i <= 1; i += 2){
        for (let j = 1; ; j++){

            if(X+(i*j) == -1 || X+(i*j) == W || Y+(i*j) == -1 || Y+(i*j) == H ||
            S[Y+(i*j)][X+(i*j)] == '#'){
                break;
            }

            if(S[Y+(i*j)][X+(i*j)] == P){
                for(let k = Math.min(Y+(i*j),Y); k <= Math.max(Y+(i*j),Y); k++){
                    S[k][k-Y+X] = P;
                }
                break;
            }
        }
    }
    //左下、右上
    for (let i = -1; i <= 1; i += 2){
        for (let j = 1; ; j++){

            if(X+(i*j) == -1 || X+(i*j) == W || Y-(i*j) == -1 || Y-(i*j) == H || 
            S[Y-(i*j)][X+(i*j)] == '#'){
                break;
            }

            if(S[Y-(i*j)][X+(i*j)] == P){
                for (let k = Math.min(X+i*j,X); k <= Math.max(X+i*j,X); k++){
                    S[Y+X-k][k] = P;
                }
                break;
            }
        }
    }
    //盤面のマス(Y, X)に石を置く
    S[Y][X] = P;
}

//盤面出力
for (let i = 0; i < H; i++){
        console.log(S[i].join(""));
}
```
## 解答コード例（JavaScript）python3の場合の模範解答参考
関数を使っているところがポイントです。
```javascript:javascript
const fs = require("fs");
const input = fs.readFileSync("/dev/stdin", "utf-8").trim();
const lines = input.split("\n");
//盤面の行数 H ,列数 W , プレイヤーの人数 N , 全プレイヤーの合計ターン数 n
const [H, W, N, n] = lines[0].split(" ").map(Number);
//盤面s
const s = lines.slice(1, H + 1).map(line => line.split(""));
//プレイヤー、石の座標を points に入れる
const points = lines.slice(H + 1).map(line => line.split(" ").map(Number));

//斜めの確認 関数定義
const check_diagonal = (x, y, s, player) => { //(石を置いたx座標, y座標, 盤面, プレイヤー)
  for (let lr1 = -1; lr1 <= 1; lr1 += 2) { //x座標の正負
      for (let lr2 = -1; lr2 <= 1; lr2 += 2) { //y座標の正負
          let i = 0;//石を置いたところから調べるマス数
          while (true) {
              i += 1;//調べるマスを増やす
              //盤面の外に出たら
              if ( 
                  x + i * lr1 < 0  || //左
                  x + i * lr1 >= W || //右
                  y + i * lr2 < 0  || //上
                  y + i * lr2 >= H    //下
              ) {
                  break;//調査終了
              }
              //自分の石があったら
              if (s[y + i * lr2][x + i * lr1] === player) {
                  //石を置くところと、その間、見つけたところまで、石を置いていく
                  for (let j = 0; j < 1 + i; j++) {
                      s[y + j * lr2][x + j * lr1] = player;//iとjの混同注意
                  } 
                  break;//調査終了//breakの位置注意
              }
              //もし穴だったら
              if (s[y + i * lr2][x + i * lr1] === "#") {
                  break;//調査終了
              }
          }
      }
  }  
};
//左右の確認 関数定義
const check_row = (x, y, s, player) => {
  for (let lr = -1; lr <= 1; lr += 2) {
      let i = 0;
      while (true) {
          i += 1;
          if (x + i * lr < 0 || x + i * lr >= W) { //盤面外
              break;
          }
          if (s[y][x + i * lr] === player) {
              for (let j = 0; j < 1 + i; j++) {
                  s[y][x + j * lr] = player;
              }
              break;
          }
          if (s[y][x + i * lr] === "#") {
              break;
          }
      }
  }  
};
//上下の確認 関数定義
const check_column = (x, y, s, player) => {
  for (let lr = -1; lr <= 1; lr += 2) {
      let i = 0;
      while (true) {
          i += 1;
          if (y + i * lr < 0 || y + i * lr >= H) { //盤面外
              break;
          }
          if (s[y + i * lr][x] === player) {
              for (let j = 0; j < 1 + i; j++) {
                  s[y + j * lr][x] = player;
              }
              break;
          }
          if (s[y + i * lr][x] === "#") {
              break;
          }
      }
  }
};

//関数実行
for (let i = 0; i < points.length; i++) {
    const [p, y, x] = points[i];
    s[y][x] = p;//石を置く
    check_row(x, y, s, p);
    check_column(x, y, s, p);
    check_diagonal(x, y, s, p);
}

//盤面出力
for (let y = 0; y < H; y++) {
    for (let x = 0; x < W; x++) {
        process.stdout.write(String(s[y][x]));
    }
    process.stdout.write("\n");
}
```
## 解答コード例（JavaScript）Rubyの場合の模範解答参考
y方向の負(-1)・0・正(1)を意味する`t`と、x方向の負(-1)・0・正(1)を意味する`s`で、縦横斜めをまとめているところがポイントです。一番簡潔に書けるコードのように思います。
```javascript:javascript
const fs = require("fs");
const input = fs.readFileSync("/dev/stdin", "utf-8").trim();
const lines = input.split("\n");
//盤面の行数 h ,列数 w , プレイヤーの人数 m , 全プレイヤーの合計ターン数 n
const [h, w, m, n] = lines[0].split(" ").map(Number);
//盤面
const board = lines.slice(1, h + 1).map(line => line.split(""));

for (let i = 1; i <= n; i++) {
    //i 回目の操作で石を置く人のプレイヤー番号 name と、その人が石を置く座標 y x 
    const [name, y, x] = lines[i + h].split(" ").map(Number);
    board[y][x] = name;//石を置く
    //縦横斜め確認
    [-1, 0, 1].forEach(t => { //t y方向の正負
        [-1, 0, 1].forEach(s => { //s x方向の正負
            let wid = 1;//移動マス数width
            let reversible = false;//はさんだマスに自分の石を置くか
           
            while (true) {
                let ny = y + t * wid;//調べる先のy座標
                let nx = x + s * wid;//調べる先のx座標

                //盤面内でない、または、穴だったら
                if (!(0 <= ny && ny <= h - 1 && 0 <= nx && nx <= w - 1) ||
                board[ny][nx] === '#') {
                    break;//そこで終わり
                }
               
                //自分の石があったら
                if (board[ny][nx] === name) {
                    reversible = true;//はさんだマスに自分の石をおく
                    break;
                }
               
                wid += 1;//次のマスを調べる
            } //while
            
            //はさんだマスに自分の石をおく
            if (reversible) {
                for (let w = 0; w < wid - 1; w++) { //不等号注意
                    let ny = y + t * (w + 1);
                    let nx = x + s * (w + 1);
                    board[ny][nx] = name;
                }
            }   
        });
    });
}

board.forEach(row => console.log(row.join('')));
```

## 解答コード例（JavaScript）
初めて解いてみたコードです。縦横斜めを一つ一つ書いているので、少し長いです。
```javascript:javascript
const fs = require("fs");
const input = fs.readFileSync("/dev/stdin", "utf-8").trim();
const lines = input.split("\n");
//盤面の行数 H ,列数 W , プレイヤーの人数 N , 全プレイヤーの合計ターン数 n 
const [H, W, N, n] = lines[0].split(" ").map(Number);
//盤面を作る
let board = lines.slice(1,H + 1).map(line => line.split(""));

//プレイヤーは次の操作を 1 回ずつ交互に合計 N 回繰り返し
for (let h = 1; h <= n; h++) {
 
    //i 回目の操作で石を置く人のプレイヤー番号 p_i と、その人が石を置く座標 Y_i X_i 
    let [p, Y, X] = lines[H + h].split(" ").map(Number);
   
    //盤面のマス ( Y_i, X_i ) に自分の石を置く
    board[Y][X] = p;

    //縦横
    //石を置いたY行目について、Y固定
    //石を置いた左側、盤面の中を調べる
    for (let i = X - 1; i >= 0; i--) {
        //調べる場所が穴の空いているマス　または　Xの左隣が自分の石ならば
        if (board[Y][i] === '#' || board[Y][X - 1] === p) {
            //そこで終わり
            break;
        //調べる場所に自分の石がある　
        } else if (board[Y][i] === p) {
            //調べた場所の１つ右に何もない または　相手の石がある
            if  (board[Y][i + 1] !== p) {
                //中に石を置く。調べた１つ右からはじめに石を置いた手前まで
                for (let j = i + 1; j < X; j++) {
                    board[Y][j] = p;
                }
                // 新たに置いた石によってさらに石が置けるようになった場合でも
                // その時点で操作を終える。
                break;
            }    
        }
    }
    
    //同様に石を置いた右側を調べる
    for (let i = X + 1; i < W; i++) {
        if (board[Y][i] === '#' || board[Y][X + 1] === p) {
            break;
        //調べる場所に自分の石がある
        } else if (board[Y][i] === p) {
            //１つ左に何もない　または　相手の石
            if (board[Y][i - 1] !== p){
                //中に石を置く。調べたところから左へ、石を置いた手前まで
                for (let j = i - 1; j > X; j--) {
                    board[Y][j] = p;
                }
                // 新たに置いた石によってさらに石が置けるようになった場合でも
                // その時点で操作を終える。
                break;
            }    
        }
    }
    
    //石を置いたX列目について
    //同様に石を置いた上側へ 盤面の中
    for (let i = Y - 1; i >= 0; i--) {
        if (board[i][X] === '#' || board[Y - 1][X] === p) {
            break;
        //自分の石があるかつ（間に何もないまたは相手の石）
        } else if (board[i][X] === p) {
            if (board[i + 1][X] !== p) {
                //中に石を置く
                for (let j = i + 1; j < Y; j++) {
                    board[j][X] = p;
                }
                // 新たに置いた石によってさらに石が置けるようになった場合でも
                // その時点で操作を終える。
                break;
            }    
        }
    }
    
    //同様に石を置いた下側へ
    for (let i = Y + 1; i < H; i++) {
        if (board[i][X] === '#' || board[Y + 1][X] === p) {
            break;
        //盤面の中かつ自分の石があるかつ(間に何もないまたは相手の石）
        } else if (board[i][X] === p) {
            if (board[i - 1][X] !== p) {
                //中に石を置く
                for (let j = i - 1; j > Y; j--) {
                    board[j][X] = p;
                }
                // 新たに置いた石によってさらに石が置けるようになった場合でも
                // その時点で操作を終える。
                break;
            }    
        }
    }
    
    //斜め
    //右斜め下へ調べる
    for (let i = 1; Y + i < H && X + i < W; i++) {
        if (board[Y + i][X + i] === '#' || board[Y + 1][X + 1] === p) {
            break;
        //自分の石がある　かつ　はさんだマスが'.'
        } else if (board[Y + i][X + i] === p) {
            //斜めに自分の石ではさんだマスの間に自分の石を置く
            if (board[Y + i - 1][X + i - 1] !== p) {
                for (let j = 1; j < i; j++) {
                    board[Y + j][X + j] = p;
                }
                break;
            }    
        }
    }
    
    //左斜め下へ
    for (let i = 1; Y + i < H && X - i >= 0; i++) {
        if (board[Y + i][X - i] === '#' || board[Y + 1][X - 1] === p) {
            break;
        } else if (board[Y + i][X - i] === p) {
            if (board[Y + i - 1][X - i + 1] !== p) {
                for (let j = 1; j < i; j++) {
                    board[Y + j][X - j] = p;
                } 
                break;
            }    
        }    
    }
    
    //右斜め上へ
    for (let i = 1; Y - i >= 0 && X + i < W; i++) {
        if (board[Y - i][X + i] === '#' || board[Y - 1][X + 1] === p) {
            break;
        } else if (board[Y - i][X + i] === p) {
            if (board[Y - i + 1][X + i - 1] !== p) {
                for (let j = 1; j < i; j++) {
                    board[Y - j][X + j] = p;
                } 
                break;
            }    
        }    
    }
    
    //左斜め上へ
    for (let i = 1; Y - i >= 0 && X - i >= 0; i++) {
        if (board[Y - i][X - i] === '#' || board[Y - 1][X - 1] === p) {
            break;
        } else if (board[Y - i][X - i] === p) {
            if (board[Y - i + 1][X - i + 1] !== p) {
                for (let j = 1; j < i; j++) {
                    board[Y - j][X - j] = p;
                } 
                break;
            } 
        }
    }
    
}//for h

//操作を終えた後の盤面を出力
console.log(board.map(masu => masu.join("")).join("\n"));
```