Kaiのプログラミング本レビュー

主にプログラミングの本のレビューをします

LightsOutゲームのプログラミング

LightsOutという数学パズルをHTML + JavaScriptでコーディングしました。
遊べるし、解法も表示されます。

以下、コードです。

index.html

<!DOCTYPE html>
<html>
<head>
<meta http-equiv="content-language" content="ja">
<meta charset="UTF-8">
</head>

<body>
<p>
【Lights Out】<br>
いくつかのボタンがあり、その中のいくつかのボタンがオン(オレンジ色)になっている状態から、<br>
ボタンを適当に押すことによって、全てのボタンをオフ(黒色)になるようにするパズルです。<br>
【ルール】<br>
   1. ボタンを押すと、そのボタン,およびそのボタンの上下左右にあるボタンのオン、オフが切り替わります。<br>
   2. 隅の方のボタンで、上下左右にボタンが無い場合は、その部分は無視してください。<br>
</p>
<form name="myform" action="javascript:void(0)">
	<p>盤面のサイズを決めてください。(5x5だったら5と入力)<br>
	   黄色のマス目の盤面が表示されます。<br>
	   マス目をクリックすればオンオフが切り替わります。
	<input name="mytext" type="text">
	<button id="btn">送信</button>
	</p>
</form>

<form name="answer" action="javascript:void(0)">
	<p>【解法】<br>(盤面のサイズを指定した後に「解答表示」ボタンを押してください)<br>
	   (緑色のマス目の場所を順不同で押していけば全部のマス目が黒くなります。解答は複数パターンある場合もあります。)<br><br>
	<button id="answer_btn">解答表示</button>
	</p>
</form>

<div id="lightsout"></div>
<script type="text/javascript" src="lightsout.js"></script>
</body>
</html>

lightsout.js

var board_size = 0;
var board_flag = false;
// 盤面の初期化
function initBoard(size) {
  var element = document.getElementById("lightsout");
  while (element.firstChild) {
    element.removeChild(element.firstChild);
  }
  var df = document.createDocumentFragment();
  var base_style = "border: 1px solid black; float: left; height: 60px; width: 60px; background: orange; style: pointer";
  for (var y = 1; y <= size; y++) {
    for (var x = 1; x <= size; x++) {
      var cell = document.createElement("div");
      if (10 <= y && 10 <= x) {
        cell.id = "p" + y + x;
      } else if (10 > y && 10 > x) {
        cell.id = "p" + "0" + y + "0" + x;
      } else if (10 <= y) {
        cell.id = "p" + y + "0" + x;
      } else if (10 <= x) {
        cell.id = "p" + "0" + y + x;
      } else {
        console.log("initBoard_error!!!");
      }
      cell.setAttribute("style", base_style);
      cell.addEventListener("click", clickEvent, false);
      df.appendChild(cell);
      // sizeピース毎に改行する
      if ((x % size) === 1) {
        cell.style.clear = "both";
      }
    }
  }
  element.appendChild(df);
}
// [y, x] に相当する DOM の element を得る。
function getElement(y, x) {
  var ret_val;
  if (10 <= y && 10 <= x) {
    ret_val = "p" + y + x;
  } else if (10 > y && 10 > x) {
    ret_val = "p" + "0" + y + "0" + x;
  } else if (10 <= y) {
    ret_val = "p" + y + "0" + x;
  } else if (10 <= x) {
    ret_val = "p" + "0" + y + x;
  } else {
    console.log("error!!!");
  }
  return document.getElementById(ret_val);
}
// マウスクリック時の処理。
function clickEvent() {
  var element = document.getElementById(this.id);
  var y = parseInt(element.id.substr(-4, 2));
  var x = parseInt(element.id.substr(-2, 2));
  changeBackground(y, x);
  if (y - 1 >= 1) changeBackground(y - 1, x);
  if (y + 1 <= board_size) changeBackground(y + 1, x);
  if (x - 1 >= 1) changeBackground(y, x - 1);
  if (x + 1 <= board_size) changeBackground(y, x + 1);
}
// 指定のマス目の色を変える。
function changeBackground(y, x) {
  var f = getElement(y, x);
  var col = f.style.backgroundColor;
  if (col == "black") {
    f.style.backgroundColor = "orange";
  } else if (col == "orange") {
    f.style.backgroundColor = "black";
  } else {
    console.log("color_error!!!");
  }
}
onload = function () {
  var btn = document.getElementById('btn');
  var answer_btn = document.getElementById('answer_btn');
  btn.addEventListener('click', function () {
    var suuti_data = document.myform.mytext.value;
    if (suuti_data.match(/^[0-9]+$/) && 1 <= suuti_data && suuti_data <= 15) {
      board_size = document.myform.mytext.value;
      board_flag = true;
    } else {
      alert("正の整数(1~15)を入力してください");
    }
  })
  answer_btn.addEventListener('click', function () {
    correctAnswer();
  })
};
////////
(function () {
  // targetを監視してtrueになったら関数を実行
  (function observe() {
    if (board_flag == true) {
      // 関数実行
      execute();
      board_flag = false;
    } else {}
    setTimeout(observe, 1);
  })();
  // trueになったら実行する処理
  function execute() {
    initBoard(board_size);
  }
})();

function correctAnswer() {
  var element = document.getElementById("lightsout");
  while (element.firstChild) {
    element.removeChild(element.firstChild);
  }
  var top_df = document.createDocumentFragment();
  var i;
  var j;
  var pattern = 1;
  var board = [];
  var play_board = [];
  var tmp;
  var x;
  var y;
  for (i = 0; i < board_size; i++) {
    board[i] = [];
    play_board[i] = [];
    for (j = 0; j < board_size; j++) {
      board[i][j] = 0;
      play_board[i][j] = 0;
    }
  }
  /* 最初の1段目の全パターンを求める */
  for (i = 0; i < board_size; i++) {
    pattern *= 2;
  }
  /* 最初の1段目の全パターン数だけ繰り返し */
  for (i = 0; i < pattern; i++) {
    /* 0で初期化 */
    for (j = 0; j < board_size; j++) {
      board[0][j] = 0;
    }
    /* 2進数の処理 */
    tmp = i; /* 今何回目かを保存 */
    j = board_size - 1; /* カウンタの初期化 */
    while (tmp != 0) { /* 0になるまで繰り返し */
      board[0][j] = tmp % 2; /* 2で割った余りを代入 */
      tmp = Math.floor(tmp / 2) /* 次の桁へ */
      j--; /* デクリメント */
    }
    /* パターンを求める */
    for (y = 0; y < board_size - 1; y++) {
      for (x = 0; x < board_size; x++) {
        tmp = board[y][x]; /* 初期化 */
        if (x != 0) tmp += board[y][x - 1];
        if (x != board_size - 1) tmp += board[y][x + 1];
        if (y != 0) tmp += board[y - 1][x];
        if (tmp % 2 == 0) {
          board[y + 1][x] = 1;
        } else {
          board[y + 1][x] = 0;
        }
      }
    }
    /* 確認用の盤面の初期化 */
    for (var ii = 0; ii < board_size; ii++) {
      for (var jj = 0; jj < board_size; jj++) {
        play_board[ii][jj] = 0;
      }
    }
    /* 解を求める  */
    for (y = 0; y < board_size; y++) {
      for (x = 0; x < board_size; x++) {
        /* 押してたら */
        if (board[y][x] == 1) {
          change_board(play_board, board_size, x, y); /* ボタンを押す */
        }
      }
    }
    /* 盤面が全て1だったら */
    if (check_board(play_board, board_size) == 1) {
      /* 盤面を子ノードに挿入 */
      top_df.appendChild(nodeCreate_func(board));
    }
  }
  element.appendChild(top_df);
}
/***
 *** (i,j)を押した時の盤面を変更する
 ***/
function change_board(board, size, xx, yy) {
  /* 盤面の変更 */
  if (xx != 0) {
    if (board[yy][xx - 1] == 0) {
      board[yy][xx - 1] = 1;
    } else if (board[yy][xx - 1] == 1) {
      board[yy][xx - 1] = 0;
    } else {
      console.log("cahnge_board_error!!!");
    }
  }
  if (xx != size - 1) {
    if (board[yy][xx + 1] == 0) {
      board[yy][xx + 1] = 1;
    } else if (board[yy][xx + 1] == 1) {
      board[yy][xx + 1] = 0;
    } else {
      console.log("cahnge_board_error!!!");
    }
  }
  if (yy != 0) {
    if (board[yy - 1][xx] == 0) {
      board[yy - 1][xx] = 1;
    } else if (board[yy - 1][xx] == 1) {
      board[yy - 1][xx] = 0;
    } else {
      console.log("cahnge_board_error!!!");
    }
  }
  if (yy != size - 1) {
    if (board[yy + 1][xx] == 0) {
      board[yy + 1][xx] = 1;
    } else if (board[yy + 1][xx] == 1) {
      board[yy + 1][xx] = 0;
    } else {
      console.log("cahnge_board_error!!!");
    }
  }
  if (board[yy][xx] == 0) {
    board[yy][xx] = 1;
  } else if (board[yy][xx] == 1) {
    board[yy][xx] = 0;
  } else {
    console.log("cahnge_board_error!!!");
  }
}
/***
 *** 盤面が全て1かをチェックする
 ***/
function check_board(board, size) {
  var count = 0; /* 全部1か確認する用のカウンタ */
  var flag = 0; /* 全部1か確認する用のフラグ */
  var x, y; /* 座標用変数 */
  /* 全部1か確認 */
  for (y = 0; y < size; y++) {
    for (x = 0; x < size; x++) {
      if (board[y][x] == 1) count++; /* 全部1だったらカウント */
    }
  }
  if (count == size * size) flag = 1; /* 全部1だったら終了 */
  return flag;
}
// 盤面の初期化
function nodeCreate_func(board) {
  var df = document.createDocumentFragment();
  var base_style_orange = "border: 1px solid black; float: left; height: 60px; width: 60px; background: orange; style: pointer";
  var base_style_green = "border: 1px solid black; float: left; height: 60px; width: 60px; background: green; style: pointer";
  var base_style_white = "float: left; height: 60px; width: 60px; background: white; style: pointer";
  for (var y = 1; y <= board_size; y++) {
    for (var x = 1; x <= board_size; x++) {
      var cell = document.createElement("div");
      if (10 <= y && 10 <= x) {
        cell.id = "p" + y + x;
      } else if (10 > y && 10 > x) {
        cell.id = "p" + "0" + y + "0" + x;
      } else if (10 <= y) {
        cell.id = "p" + y + "0" + x;
      } else if (10 <= x) {
        cell.id = "p" + "0" + y + x;
      } else {
        console.log("nodeCreate_func_error!!!");
      }
      if (board[y - 1][x - 1] == 0) {
        cell.setAttribute("style", base_style_orange);
      } else if (board[y - 1][x - 1] == 1) {
        cell.setAttribute("style", base_style_green);
      } else {
        console.log("nodeCreate_func_orange_green_error!!!");
      }
      df.appendChild(cell);
      // board_sizeピース毎に改行する
      if ((x % board_size) === 1) {
        cell.style.clear = "both";
      }
    }
    if (y == board_size) {
      for (var x = 1; x <= board_size; x++) {
        var cell = document.createElement("div");
        if (10 <= y) {
          if (10 <= x) {
            cell.id = "p" + (board_size + 1) + x;
          } else if (10 > x) {
            cell.id = "p" + (board_size + 1) + "0" + x;
          } else {
            console.log("board_size_x_error!!!");
          }
        } else if (10 > y) {
          if (10 <= x) {
            cell.id = "p" + "0" + (board_size + 1) + x;
          } else if (10 > x) {
            cell.id = "p" + "0" + (board_size + 1) + "0" + x;
          } else {
            console.log("board_size_x_error!!!");
          }
        } else {
          console.log("board_size_y_error!!!");
        }
        cell.setAttribute("style", base_style_white);
        df.appendChild(cell);
        // board_sizeピース毎に改行する
        if ((x % board_size) === 1) {
          cell.style.clear = "both";
        }
      }
    }
  }
  return df;
}