LoginSignup
1
1

More than 3 years have passed since last update.

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

Posted at

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

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配列にしておく。
代わりに各マスには座標を持ってもらう。

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

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

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

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

[ { 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)の判定を作ってみる。

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)の条件をクリアできれば目的達成。

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()」かけると配列自体が変更されるけど、使い捨てるので良しとする。

  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);

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

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

/* ===== 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));

以上。

1
1
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
1
1