読者です 読者をやめる 読者になる 読者になる

thagwen’s blog

現役高校2年生のthagwenの趣味・勉強日記

2017.04.16 JSで数独プログラムを作りたい part2

JSで数独プログラムを作りたい part2

前回はロジックについて考えていきましたね。今回は実際にプログラムを組んでみます。

とりあえず、盤面をどう表現するかですが…。今回は2次元配列で表現することにしました。そのようにすることによって、例えば(3, 4)の数字が知りたいときに

var number = board[3][4];

というようにアクセスしやすくなるからです。


数字がないことを表現するのに0を用いることにします。配列に保存するオブジェクトの方が数値型(Number型)だからです。

盤面サイズですが、拡張すると宣言したものの、とりあえず9マスとしておきます。教科書の3倍の大きさで通常のナンプレと同じサイズです。

最後に解答を確認するために、解答を格納する変数を用意しておきます。以上がグローバル変数です。

var EMPTY = 0;
var SIZE = 9;
var answer = [];


ある縦列において、ある数字が置けるかどうかを確認するメソッドを定義します。同時に横列についてのメソッドも定義します。どちらも同じようなメソッドです。

function checkColumn(board, num, y) {
  for ( var i = 0; i < SIZE; i=(i+1)|0 ) {
    if ( board[i][y] === num ) {
      return false;
    }
  }
  return true;
}

function checkRow(board, num, x) {
  for ( var i = 0; i < SIZE; i=(i+1)|0 ) {
    if ( board[x][i] === num ) {
      return false;
    }
  }
  return true;
}

考え方は単純で、例えば横列について調べるときは、x=n(n=0,1,2...,SIZE)の盤面の数値を取得して、調べたい数値と比べます。それが等しい場合は、すでにその数字が盤面上にあることを示すので、置けない(false)を返します。最後まで横列を調べ切って同じ値が一つもない場合は、置くことができるのでtrueを返します。

引数boardは常に2次元配列で渡されるものとします。グローバル変数削減のためです。


配列は[0,1,2,3...]という風に0から始まるので、一番左上のマスは[0][0]と表現されます。


グループについても定義します。ループ処理をしやすくするために、グループに属する特定の座標からグループの一番左上の座標を取得することを考えます。

グループの左上の数字は、

groupX = int(x / 3) * 3
groupY = int(y / 3) * 3

で求められます。

function checkGroup(board, num, x, y) {
  var groupX = Math.floor(x/3)*3;
  var groupY = Math.floor(y/3)*3;
  for ( var i = 0; i < 3; i=(i+1)|0 ) {
    for ( var j = 0; j < 3; j=(j+1)|0 ) {
      if ( board[groupX+i][groupY+j] === num ) {
        return false;
      }
    }
  }
  return true;
}

ここから縦横に3マスづつ調べて横列、縦列の査定と同じように同じ数字がないことを調べます。同じ数字があるときはFalse、なければTrueが返ってきます。

このように座標は表せます。

function isEmpty(board, x, y) {
  if ( board[x][y] === EMPTY ) {
    return true;
  }
  return false;
}

そしてある位置に数字がないことを調べます。当然、既に存在する数字は置き換えることができないので、事前にそれを知る必要があります。処理としては、単純に指定座標の数字が先ほど定義したEMPTY(=0)であることを確認するだけです。

function checkPutting(board, num, x, y) {
  if ( checkColumn(board, num, y)
    && checkRow(board, num, x)
    && checkGroup(board, num, x, y)
    && isEmpty(board, x, y) ) {
    return true;
  }
  return false;
}

簡単にいうと先ほど定義した4つの関数を同時に行うことで、ある数字が置けるかどうかを判別します。


とりあえず、今日はここまでとします。


実はここまでに定義した関数のほとんどは教科書に書いてあった関数を一般化・拡張したものです。この後の実際に盤面に数字を置いていく関数は教科書では一切触れられていないことなので、完成までに時間がかかると想定されます。

ちょくちょく更新していきますので、楽しみにしていてくださいね。


今日の学習

古文解釈の仕方 第1講