paizaやってみた　＃1 「マップの扱い3 - マップの判定・縦横斜めPHP編」(Bランク)

```php
<?php
//マップの扱い3

/*　問題文
マップの行数 H と列数 W とマップを表す H 行 W 列の文字列 S_1 ... S_H と y , x 座標 が与えられる
与えられた座標のマス(y, x) と、 (y, x) と同じ縦・横・斜めの列に存在する全てのマスについて、、
次の処理をおこなった後の盤面を出力してください。

マスに書かれている文字が "." の場合は "#" に、"#" の場合は "." に書き換える。

なお、マスの座標系は左上端のマスの座標を ( y , x ) = ( 0 , 0 ) とし、
下方向が y 座標の正の向き、右方向が x 座標の正の向きとします。
マス(y,x) と同じ斜めの列とは整数 a を用いて (y+a,x+a), (y+a,x-a), (y-a,x-a), (y-a,x+a) のいずれかで表されるマスの集合です。
*/

/* 以下、投稿者の回答例
行数：$h
列数：$w
与えられる文字列を格納する配列：$s[]
与えられる基準の座標(y,x)と格納する変数：($y,$x)　
左上端 = (0,0)
$cy:ループ内での現在のy座標の数値
$cx:ループ内での現在のx座標の数値

*/

//行数と列数の取得
$hw = explode(" ",rtrim(fgets(STDIN)));
$h = (int)$hw[0];
$w = (int)$hw[1];

$s = [];

//文字列取得
for($i = 0; $i < $h; $i++){
  $s[$i] = mb_str_split(rtrim(fgets(STDIN))); //$s[$i][$s[0], $s[1]...]
}

//座標の取得
$yx = explode(" ",rtrim(fgets((STDIN))));
$y = (int)$yx[0];
$x = (int)$yx[1];

//横方向の処理
for($cx = 0; $cx < $w; $cx++){ 
  if($s[$y][$cx] === "#"){
    $s[$y][$cx] = ".";
  }elseif($s[$y][$cx] === "."){
    $s[$y][$cx] = "#";
  }
}

//縦方向の処理
for($cy = 0; $cy < $h; $cy++){ 
  if($s[$cy][$x] === "#"){
    $s[$cy][$x] = ".";
  }elseif($s[$cy][$x] === "."){
    $s[$cy][$x] = "#";
  }
}

//左上から右下の処理
$diff = $y - $x; //この処理では$y-$xの値は常に一定になる
for($cy = 0; $cy < $h; $cy++){
  $cx = $cy - $diff;　//

  if($cx >= 0 && $cx < $w){
    if($s[$cy][$cx] === "#"){
      $s[$cy][$cx] = ".";
    }elseif($s[$cy][$cx] === "."){
      $s[$cy][$cx] = "#";
    }
  }
}

//右上から左下の処理
$sum = $y + $x; //この処理では$y+$xの値は常に一定になる
for($cy = 0; $cy < $h; $cy++){
  $cx = $sum - $cy;

  if($cx >= 0 && $cx < $w){
    if($s[$cy][$cx] === "#"){
      $s[$cy][$cx] = ".";
    }elseif($s[$cy][$cx] === "."){
      $s[$cy][$cx] = "#";
    }
  }
}

//基準点の処理
if($s[$y][$x] === "#"){
  $s[$y][$x] = ".";
  }elseif($s[$y][$x] === "."){
    $s[$y][$x] = "#";
  }


for($i = 0; $i < $h; $i++){
    echo implode("", $s[$i]) . "\n";
}
```

変数名は適当につけてます。わかりづらくてすいません。💦

ざっくりした考え方は、
1. 入力値の取得(paiza恒例)
2. 基準座標からみて、同じ縦横斜めの列の値の変更と配列への格納
3. 基準座標の値の変更と配列への格納
4. 配列を結合し、文字列として出力
となっています。


#1. 入力値の取得
詳しい入力値の処理はpaizaに載ってますので割愛します。

ここでの重要なポイントは、mb_str_splitを使用して、
文字列を構成する文字を配列として格納することです。
配列にすることで、変換したい文字を座標で指定することができます。

```php
for($i = 0; $i < $h; $i++){
  $s[$i] = mb_str_split(rtrim(fgets(STDIN))); 
}
//$s[$y][$x]のような形で文字の座標を指定する
```

#2.　基準座標からみて、同じ縦横斜めの列の値の変更と配列への格納

```php
//左上から右下の処理
$diff = $y - $x; //この処理では$y-$xの値は常に一定になる
for($cy = 0; $cy < $h; $cy++){
  $cx = $cy - $diff;　//

  if($cx >= 0 && $cx < $w){
    if($s[$cy][$cx] === "#"){
      $s[$cy][$cx] = ".";
    }elseif($s[$cy][$cx] === "."){
      $s[$cy][$cx] = "#";
    }
  }
}
```
基準座標からみて縦、横、左上から右下、右上から左下でそれぞれ処理を作成する。
縦と横の処理は簡単でしたが、斜めの処理はかなり手こずりました笑
コツとしては、それぞれの座標を特定する式を考えることです。
```php:座標の特定に使う式
$diff = $y - $x; //この処理では$y-$xの値は常に一定になる
```
紙にグリッドと実数値を書いて、シミュレーションするとわかりやすいです！

#3. 基準座標の値の変更と配列への格納

基準座標は縦、横、左上から右下、右上から左下それぞれで
文字が変更されるため、2の処理を終えた時点で入力値に戻っています。
そのため、最後に基準座標の文字を変更することで、配列への格納が
完了します。

#4. 配列を結合し、文字列として出力

ここで、先ほどまで格納した配列内の変更後の文字を、再度文字列として
再構成し、出力します。

```php
for($i = 0; $i < $h; $i++){
    echo implode("", $s[$i]) . "\n";
}
```

以上がこの問題の解説でした。

この問題、paizaにPHPでの回答例がなく、何回も試行錯誤したり、ググったり、Geminiに聞いたりしてようやくたどり着きました。(この問題は練習問題なので、カンニングには当たりません。私の現ランクはCのままです。)

今回が初めての投稿でしたので、至らぬところあったと思いますが、
参考になれば幸いです。あとコメントいただけると嬉しいです。