オセロの裏返し判定書いてみる

思考ゲームとして、オセロの裏返し判定を書いてみる。

```javascript
const fields = [...Array(8*8)].map((o,i)=>({ x:i%8, y:parseInt(i/8), c:null }));
const [ w, b ] = ["white","black"];
```

とりあえず8×8の盤面がないとお話にならないので準備。
2D配列でもいいんだろうけど、処理がめんどくさそうなので1D配列にしておく。
代わりに各マスには座標を持ってもらう。

白黒はよく使うはずなので定数化。

```javascript
const find = o=> (e,i,arr)=>e.x==o.x&&e.y==o.y ;
```

目的のマスを見つける処理も多用しそうなので先に定義。

最初に真ん中の4マスにコマを置く。

```javascript
[ { x:3, y:3, c:w },{ x:4, y:3, c:b },
  { x:3, y:4, c:b },{ x:4, y:4, c:w } ]
.forEach(o=>fields.find(find(o)).c=o.c);
```

オセロの裏返し判定条件は、

(1)対象のマスを含む縦横斜めの1直線上で
(2)隣接した連続するマスに対象以外の色が配置されていて
(3)その終端に対象と同じ色がある

ってことなので、まずは(2)(3)の判定を作ってみる。

```javascript
const reversibles = (v, line)=>{
  const partial = line.filter((e,i)=>i>line.findIndex(find(v)));
  if(partial.length==0) return [];
  let str = partial.map(e=>(!e.c)?" ":e.c.charAt(0)).reduce((a,n)=>a+n);
  const c = v.c.charAt(0);
  str=str.replace(new RegExp("^([^ "+ c +"]+" + c + ")?.*$"), "$1");
  return partial.filter((e,i)=>i<str.length-1);
};
```

こんな感じ。
1直線から自分より後ろの部分を切り出して判定。
正規表現使うと楽だった。

ここでは1直線上で裏返し可能なマスを配列で返すように実装。

後は(1)の条件をクリアできれば目的達成。

```javascript
const reversi = o=>{
  const pos = fields.find(find(o));
  if (pos.c) return -1;
  
  const horizontal = fields.filter(e=>e.y==o.y);
  const vertical = fields.filter(e=>e.x==o.x);
  const leftup = fields.filter(e=>e.x-e.y==o.x-o.y);
  const leftdown = fields.filter(e=>e.x+e.y==o.x+o.y);
  
  const result = []
        .concat(reversibles(o, horizontal))
        .concat(reversibles(o, horizontal.reverse()))
        .concat(reversibles(o, vertical))
        .concat(reversibles(o, vertical.reverse()))
        .concat(reversibles(o, leftup))
        .concat(reversibles(o, leftup.reverse()))
        .concat(reversibles(o, leftdown))
        .concat(reversibles(o, leftdown.reverse()))
  ;
  if(result.length) {
    pos.c=o.c;
    result.forEach(e=>fields.find(find(e)).c=o.c);
  }
  return result.length;
};
```

で、こうなる。
-1が返ってきたらそのマスは使用中。
0だったら裏返せるマスがないのでそのマスは使用不可。

配列に｢reverse()｣かけると配列自体が変更されるけど、使い捨てるので良しとする。

```javascript
  const horizontal = fields.filter(e=>e.y==o.y);
  const vertical = fields.filter(e=>e.x==o.x);
```

縦横の直線はすぐにわかるが、

```javascript
  const leftup = fields.filter(e=>e.x-e.y==o.x-o.y);
  const leftdown = fields.filter(e=>e.x+e.y==o.x+o.y);
```

x、yの差が同一のマスを選択すると左肩上がり、右肩下がりの直線が、
x、yの和が同一のマスを選択すると左肩下がり、右肩上がりの直線が、
それぞれ取れるのは常識だよね？

最後に出来上がった物をテストしてみる。

```javascript
/* ===== test ===== */

// (3,3):white (4,3):black
// (3,4):black (4,4):white

// -- 1st：white(3,5)
console.log(reversi({x:3,y:5,c:w}));

// (3,3):white (4,3):black
// (3,4):white (4,4):white
// (3,5):white

// -- 2nd：black(2,5)
console.log(reversi({x:2,y:5,c:b}));

//             (3,3):white (4,3):black
//             (3,4):black (4,4):white
// (2,5):black (3,5):white

// -- 3rd(1)：white(2,5) -! already occupied
console.log(0>reversi({x:2,y:5,c:w})?"occupied":"not occupied");

// -- 3rd(2)：white(0,0) -! not available
console.log(0==reversi({x:0,y:0,c:w})?"not yet":"go on");

const arr2string = arr=>{
  let str = "[\n", sep = "";
  arr.forEach((e,i)=>{
    str += sep + "(" + i + "){ x:" + e.x + ", y:" + e.y + ", c:" + e.c + " }";
    sep = ", " + (i%8==7?"\n":"");
  });
  return str + "\n]";
};
console.log(arr2string(fields));
```

以上。