[Game] Javascriptでナンプレ、自動解答機能

2016年12月12日

Javascript ゲーム テクノロジー プログラミング 特集

ナンプレの基本インターフェイスを前回構築したんですが、やはり自動回答と自動問題作成機能を作らないとチャンとしたゲームとは言えないでしょう。 そこで、今回は問題を自動で回答する、機能に挑戦してみました。 最初に行っておきますが、ナンプレには、レベルがあって、初級、中級、上級とあり、今回は初級問題にのみ対応できるプログラムになっています。

前回のおさらい

前回は、ナンプレの基本表示部分と、入力処理、回答を入力した後の正解チェック機能を作ってます。 基本的にjavascriptだけで動作する事を目的にしてますが、問題読み込み機能でajaxを使っているので、ファイル表示では動作しないので、読み込み機能を使うときは仮想環境か、サーバーに配置して使ってください。

今回の機能概要

「初級の自動回答機能」として、以下の処理を実現してます。
1. 空欄の箇所に入る可能性のある数値を仮登録 2. 縦、横、3x3の箱でのユニーク値チェック 3. チェックを複数回繰り返す反復処理

ソースコード

今回の変更点は「numberplace.js」のみだけですが、とりあえず全てのソースコードを載せておきます。 <!DOCTYPE html> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1" /> <title>NumberPlace</title> <meta http-equiv="Pragma" content="no-cache"> <meta http-equiv="Cache-Control" content="no-cache"> <meta http-equiv="Expires" content="Thu, 01 Dec 1994 16:00:00 GMT"> <link type='text/css' rel='stylesheet' href='css/numberplace.css' /> <script type='text/javascript' src='js/lib.js'></script> <script type='text/javascript' src='js/numberplace.js'></script> </head> <body class='design_black' data-transition='scroll_stop'> <h1>NumberPlace</h1> <hr> <div id='game'> <table class="numberplace"> <tr><td></td><td></td><td></td> <td></td><td></td><td></td> <td></td><td></td><td></td></tr> <tr><td></td><td></td><td></td> <td></td><td></td><td></td> <td></td><td></td><td></td></tr> <tr><td></td><td></td><td></td> <td></td><td></td><td></td> <td></td><td></td><td></td></tr> <tr><td></td><td></td><td></td> <td></td><td></td><td></td> <td></td><td></td><td></td></tr> <tr><td></td><td></td><td></td> <td></td><td></td><td></td> <td></td><td></td><td></td></tr> <tr><td></td><td></td><td></td> <td></td><td></td><td></td> <td></td><td></td><td></td></tr> <tr><td></td><td></td><td></td> <td></td><td></td><td></td> <td></td><td></td><td></td></tr> <tr><td></td><td></td><td></td> <td></td><td></td><td></td> <td></td><td></td><td></td></tr> <tr><td></td><td></td><td></td> <td></td><td></td><td></td> <td></td><td></td><td></td></tr> </table> </div> <div id="key"> <table class="numberKey"> <tr> <td data-key="1">1</td> <td data-key="2">2</td> <td data-key="3">3</td> </tr> <tr> <td data-key="4">4</td> <td data-key="5">5</td> <td data-key="6">6</td> </tr> <tr> <td data-key="7">7</td> <td data-key="8">8</td> <td data-key="9">9</td> </tr> <tr> <td class="clear" data-key="" colspan="3">clear</td> </tr> </table> </div> <hr> <div> <button id="clear">Clear</button> <button id="solve">Solve</button> <button id="finish">Finish</button> </div> <div> <button id="load">Load</button> <button id="save">Save</button> </div> <!-- <div> <button id="programSave">Program-Save</button> <button id="programLoad">Program-Load</button> </div> --> <hr> <ol id="lists"> </ol> </body> </html> body,html{ width:100%; height:100%; } body{ text-align:center; } #game{ display:inline-block; box-shadow:4px 4px 4px rgba(0,0,0,0.4); margin:auto auto; } table{ border-collapse:collapse; } td{ padding:0; margin:0; } /*numberPlace*/ .numberplace td{ width:32px; height:32px; border:1px solid #666; cursor:pointer; text-align:center; vertical-align:middle; background-color:white; font-size:20px; font-weight:bold; } .numberplace td:hover{ background-color:#DDD; } .numberplace tr:nth-child(1){ border-top:2px solid #000; } .numberplace tr:nth-child(3){ border-bottom:2px solid #000; } .numberplace tr:nth-child(6){ border-bottom:2px solid #000; } .numberplace tr:nth-child(9){ border-bottom:2px solid #000; } .numberplace td:nth-child(1){ border-left:2px solid #000; } .numberplace td:nth-child(3){ border-right:2px solid #000; } .numberplace td:nth-child(6){ border-right:2px solid #000; } .numberplace td:nth-child(9){ border-right:2px solid #000; } /*action*/ #game .numberplace td{ background-color:#FFF; } #game .numberplace td[data-enable="false"]{ background-color:#EEE; } #game .numberplace td[data-enable="active"]{ background-color:#FEE; } #game .numberplace td[data-enable="target"]{ background-color:#FCC; } #game .numberplace td[data-enable="bad"]{ background-color:#CCF; } #game .numberplace td[data-enable="complete"]{ background-color:#FFC; } /*numberKey*/ #key{ position:absolute; display:none; box-shadow:10px 10px 10px rgba(0,0,0,0.4); margin:8px; } .numberKey td{ width:40px; height:40px; background-color:blue; color:white; cont-weight:bold; text-align:center; vertical-align:middle; font-size:32px; border:1px solid white; cursor:pointer; } .numberKey td.clear{ width:auto; background-color:#666; font-size:24px; } .numberKey td:hover{ background-color:red; } .numberKey td:active{ background-color:black; } /*etc*/ button{ margin:8px; font-size:12px; font-weight:bold; cursor:pointer; border:1px solid #CCC; border-radius:4px; padding:8px; } button:hover{ opacity:0.5; } #lists li{ text-align:left; cursor:pointer; font-size:24px; } !(function(){ var $$={}; /** * Library --------------------- */ /** * Event-Set * param @ t : Target-element * param @ m : mode ["onload"->"load" , "onclick"->"click"] * param @ f : function **/ $$.eventAdd=function(t, m, f){ //other Browser if (t.addEventListener){t.addEventListener(m, f, false)} //IE else{ if(m=='load'){ var body = document.body; if(typeof(body)!='undefined'){body = w;} if((typeof(onload)!='undefined' && typeof(body.onload)!='undefined' && onload == body.onload) || typeof(eval(onload))=='object'){ t.attachEvent('on' + m, function() { f.call(t , window.event); }); } else{f.call(t, w.event)} } else{t.attachEvent('on' + m, function() { f.call(t , window.event); })} } }; /** * Get URL-property * return @ [url , domain , querys{}]; **/ $$.urlProperty=function(url){ if(!url){url=location.href} var res = {}; var urls = url.split("?"); res.url = urls[0]; res.domain = urls[0].split("/")[2]; res.querys={}; if(urls[1]){ var querys = urls[1].split("&"); for(var i=0;i<querys.length;i++){ var keyValue = querys[i].split("="); if(keyValue.length!=2||keyValue[0]===""){continue} res.querys[keyValue[0]] = keyValue[1]; } } return res; }; /** * Ajax * $$.ajax.set({ * url:"**", * method:"POST", * async:true, * query:{}, * querys:[], * onSuccess:function(){} * }); **/ $$.ajax = { createHttpRequest:function(){ //Win ie用 if(window.ActiveXObject){ //MSXML2以降用; try{return new ActiveXObject("Msxml2.XMLHTTP")} catch(e){ //旧MSXML用; try{return new ActiveXObject("Microsoft.XMLHTTP")} catch(e2){return null} } } //Win ie以外のXMLHttpRequestオブジェクト実装ブラウザ用; else if(window.XMLHttpRequest){return new XMLHttpRequest()} else{return null} }, //XMLHttpRequestオブジェクト生成 set:function(options){ if(!options){return} var httpoj = new $$.ajax.createHttpRequest(); if(!httpoj){return;} //open メソッド; option = $$.ajax.setOption(options); httpoj.open( option.method , option.url , option.async ); //type //httpoj.setRequestHeader('Content-Type', option.type); if(typeof option.type != "undefined"){ httpoj.setRequestHeader('Content-Type', option.type); } else{ httpoj.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded'); } //onload-check // httpoj.onreadystatechange = this.readystate(httpoj,option); httpoj.onreadystatechange = function(){ //readyState値は4で受信完了; if (httpoj.readyState==4){ //コールバック option.onSuccess(httpoj.responseText); } }; //query整形 var data = $$.ajax.setQuery(option); //send メソッド if(data.length){ httpoj.send(data.join("&")); } else{ httpoj.send(); } }, dataOption:{ url:"", query:{}, // same-key Nothing querys:[], // same-key OK data:{}, // ETC-data event受渡用 async:"true", // [trye:非同期 false:同期] method:"POST", // [POST / GET] type:"application/x-www-form-urlencoded", // [text/javascript]... //call-back onSuccess:function(res){console.log("Success:"+res)}, onError:function(res){console.log("Error:"+res)} }, setOption:function(options){ var option = {}; for(var i in $$.ajax.dataOption){ if(typeof options[i] != "undefined"){ option[i] = options[i]; } else{ option[i] = $$.ajax.dataOption[i]; } } return option; }, setQuery:function(option){ var data = []; if(typeof option.query != "undefined"){ for(var i in option.query){ data.push(i+"="+encodeURIComponent(option.query[i])); } } if(typeof option.querys != "undefined"){ for(var i=0;i<option.querys.length;i++){ if(typeof option.querys[i] == "Array"){ data.push(option.querys[i][0]+"="+encodeURIComponent(option.querys[i][1])); } else{ var sp = option.querys[i].split("="); data.push(sp[0]+"="+encodeURIComponent(sp[1])); } } } return data; }, _:0 }; //path-info Ex):p=location.href $$.pathinfo = function(p){ var basename="", dirname=[], filename=[], ext=""; var p2 = p.split("?"); var urls = p2[0].split("/"); for(var i=0; i<urls.length-1; i++){ dirname.push(urls[i]); } basename = urls[urls.length-1]; var basenames = basename.split("."); for(var i=0;i<basenames.length-1;i++){ filename.push(basenames[i]); } ext = basenames[basenames.length-1]; return { "hostname":urls[2], "basename":basename, "dirname":dirname.join("/"), "filename":filename.join("."), "extension":ext, "query":(p2[1])?p2[1]:"", "path":p2[0] }; }; $$.urlinfo=function(uri){ if(!uri){uri = location.href} var url = uri; //URLとクエリ分離分解; var query={}; // set-query if(uri.indexOf("?")!=-1){ var sp1 = uri.split("?"); url = sp1[0]; query = $$.getQuery(sp1[1],"&","="); } // semi-colon-split else if(uri.indexOf(";")!=-1){ var sp = uri.split(";"); url = sp[0]; query = $$.getQuery(sp1[1],";","="); } //基本情報取得; var sp = url.split("/"); var selfpath = ""; for(var i=3;i<sp.length;i++){ //selfpaths.push(sp[i]); selfpath += "/"+sp[i]; } var data={ url:url, dirname:this.pathinfo(url).dirname, domain:sp[2], protocol:sp[0].replace(":",""), selfpath:selfpath, query:query }; return data; }; // ex) $$.getQuery("a=1&b=2&c=3","&","="); $$.getQuery = function(data, splitValue, keyValueSplit){ var query = {}; var sp = data.split(splitValue); for(var i=0; i<sp.length; i++){ var sp2 = sp[i].split(keyValueSplit); query[sp2[0]] = sp2[1]; } return query; }; //cookie $$.cookie = { //init-data options:{ name : 'temporary_cookie', day : 0, hour : 0, min : 1, sec : 0 }, //expires date : function(add) { if(!add){add = 0} var exp = new Date(); if(add){ exp.setTime(exp.getTime()+ add); } else{ exp.setTime(exp.getTime() + (this.options.day * 1000 * 60 * 60 * 24) + (this.options.hour * 1000 * 60 * 60) + (this.options.min * 1000 * 60) + (this.options.sec * 1000)); } return exp.toGMTString(); }, //secure-check checkSecure : function() { if (location.href.match(/^https:/)) {return true} else {return false} }, set : function(name, val, addTime) { if(!addTime){addTime = 3600;} if(!name){name = this.options.name} val = this.encode(val); if (this.checkSecure()) { document.cookie = name + "=" + val + ";expires=" + this.date(addTime) + ";secure"; } else { document.cookie = name + "=" + val + ";expires=" + this.date(addTime); } }, del : function(name){ if(!name){name = this.options.name} var exp = new Date(); exp.setTime(exp.getTime()-1); var d = exp.toGMTString(); if (this.checkSecure()) { document.cookie = name + "='';expires=" + d + ";secure"; } else { document.cookie = name + "='';expires=" + d; } }, get : function(name){ return this.val(name); }, val : function(name) { var ck0 = document.cookie.split(" ").join(""); var ck1 = ck0.split(";"); for ( var i = 0; i < ck1.length; i++) { var ck2 = ck1[i].split("="); if (ck2[0] == name) { ck2[1] = this.encode(ck2[1]); return ck2[1]; } } return ''; }, encode:function(val){ if (!val) {return ""} val = val.split("¥r") .join(""); val = val.split("¥n") .join(""); val = val.split("<") .join("-"); val = val.split("%3c").join("-"); val = val.split("%3C").join("-"); val = val.split(">") .join("-"); val = val.split("%3e").join("-"); val = val.split("%3E").join("-"); return val; } }; /** // set-value -> element [ex) $$.getElement("id","#sample")] // type @ [id , class , querySelector] // caution issue return multi-element **/ $$.getElement = function(type , str , num){ //single if(type == "id"){ return document.getElementById(str); } //multi var elements; if(type == "class"){ elements = document.getElementsByClassName(str); } else if(type == "name"){ elements = document.getElementsByName(str); } else if(type == "querySelector"){ elements = document.querySelector(str); } // return-- if(num == "multi"){ return elements; } else if(num == "first"){ return elements[0]; } }; $$.getScriptTag = function(){ var protechScript = document.getElementById("ProtechScript"); if(protechScript == null){return null} return __PROTECH_COMMON.urlinfo(protechScript.src); }; $$.getServiceData = function(service , srcInfo){ if(typeof(__PROTECH_INFO)=="undeifned" || typeof(__PROTECH_INFO.services)=="undeifned"){return null} var data = null; for(var i=0; i<__PROTECH_INFO.services.length; i++){ if(typeof __PROTECH_INFO.services[i] == "undefined"){continue} if(service && service != __PROTECH_INFO.services[i].api){continue} // solid-id if(typeof srcInfo.query.p != "undefined"){ if(typeof __PROTECH_INFO.services[i].pid == "undefined"){continue} if(srcInfo.query.p == __PROTECH_INFO.services[i].pid){ data = __PROTECH_INFO.services[i]; break; } else{continue} } // url-match if(typeof __PROTECH_INFO.services[i]["url"] == "undefined"){continue} if(__PROTECH_INFO.services[i]["url"] == location.href){ data = __PROTECH_INFO.services[i]; break; } } return data; }; /** * Servide Use Function **/ $$.getCookie = function(name){ if(!name || typeof __PROTECH != "undefined" || typeof __PROTECH.cookieName != "undefined" ){name = __PROTECH.cookieName} var cookieData = $$.cookie.get(name); if(!cookieData){ return {"pv":"","uu":"","su":""}; } else{ var sp = cookieData.split("."); return {"uu":sp[0],"su":sp[1],"pv":sp[2]}; } } /** * Log write */ $$.logWrite = function(data){ var uidCookie = $$.getCookie(); var q = [ 'mode=' + 'log', 'd=' + escape(__PROTECH[service].uKey), 'pv=' + escape(uidCookie["pv"]), 'su=' + escape(uidCookie["su"]), 'uu=' + escape(uidCookie["uu"]), 'data=' + escape(data) ]; var sc = document.createElement('script'); sc.src = __PROTECH[service].scriptPath + 'load.php'+'?t='+ (+new Date()) + "&" +q.join("&"); sc.type = 'text/javascript'; sc.async = true; document.body.appendChild(sc); console.log(sc.src); } /** * 62進数 **/ $$.hex62 = { chars:function(){ var str = ""; str += "0123456789"; str += "abcdefghijklmnopqrstuvwxyz"; str += "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; return str; }, encode:function(num){ var chars = this.chars(); var cn = chars.length; var str = []; var a1 , a2; while (num != 0) { a1 = parseInt(num / cn); a2 = num - (a1 * cn); str.unshift(chars.substr(a2,1)); num = a1; } var res = str.join(""); res = (!res)?"0":res; return res; }, decode:function(num){ var chars = this.chars(); var char2 = {}; var cn = chars.length; for (var i=0; i< cn; i++) { char2[chars[i]] = i; } var str = 0; for (var i=0; i<num.toString().length; i++) { str += char2[num.substr((i+1)*-1, 1)] * Math.pow(cn, i); } return str; } }; /** * 100進数 **/ $$.digit100 = { chars:function(){ var str = ""; str += "0123456789"; str += "abcdefghijklmnopqrstuvwxyz"; str += "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; // -+*/@_?,.;:^!#$%&'()[]{}<>=~|`¥ //str += "!"; return str; }, encode:function(num){ var chars = this.chars(); var cn = chars.length; var str = []; var a1 , a2; while (num != 0) { a1 = parseInt(num / cn); a2 = num - (a1 * cn); str.unshift(chars.substr(a2,1)); num = a1; } var res = str.join(""); res = (!res)?"0":res; return res; }, decode:function(num){ var chars = this.chars(); var char2 = {}; var cn = chars.length; for (var i=0; i< cn; i++) { char2[chars[i]] = i; } var str = 0; for (var i=0; i<num.toString().length; i++) { str += char2[num.substr((i+1)*-1, 1)] * Math.pow(cn, i); } return str; } }; // Transformation $$.getPos = function(elm){ //エレメント確認処理 if(!elm){return;} //途中指定のエレメントチェック(指定がない場合はbody) var target = document.body; //デフォルト座標 var pos={x:0,y:0}; do{ //指定エレメントでストップする。 if(elm == target){break} //対象エレメントが存在しない場合はその辞典で終了 if(typeof(elm)=='undefined' || elm==null){break} //座標を足し込む pos.x += elm.offsetLeft; pos.y += elm.offsetTop; } //上位エレメントを参照する while(elm = elm.offsetParent); //最終座標を返す return pos; }; //targetの中心座標にelmの中心座標を移動する。 $$.getPosCenter = function(elm,target){ var pos1 = $$.getPos(target); var size1 = $$.getSize(target); var size2 = $$.getSize(elm); return { x:(pos1.x+(size1.x/2)-(size2.x/2)), y:(pos1.y+(size1.y/2)-(size2.y/2)) }; }; $$.getSize = function(elm){ //対象element if(typeof(elm)=='undefined'){ if (navigator.userAgent.match("MSIE")&&document.compatMode!='BackCompat'){ elm = document.documentElement; } else{ elm = document.getElementsByTagName("body")[0]; } } //サイズ取得; var size={ x:elm.offsetWidth, y:elm.offsetHeight }; //子階層依存※下に1つのみの子を持つ場合サイズチェックを行う; if(elm.childNodes.length==1 && elm.tagName=='A'){ var chk ={ x:elm.childNodes[0].offsetWidth, y:elm.childNodes[0].offsetHeight }; if(chk.x > size.x){ size.x = chk.x; } if(chk.y > size.y){ size.y = chk.y; } } return size; }; //Emveronment $$.getWindowSize = function(){ var d={x:0,y:0}; var e; if(window.innerWidth){ document.x = window.innerWidth; document.y = window.innerHeight; } else if(navigator.userAgent.indexOf("MSIE")!=-1&&document.compatMode=='BackCompat'){ document.x = document.body.clientWidth; document.y = document.body.clientHeight; } else{ document.x = document.documentElement.clientWidth; document.y = document.documentElement.clientHeight; } return d; }; //mouse $$.getMouse = { pos:{x:0,y:0}, proc:function(e){ //IE以外のブラウザ; if(e){ this.pos={ x:e.clientX, y:e.clientY }; } //IE処理; else{ this.pos={ x:event.x, y:event.y }; } } }; window.$$LIB = $$; })(); !(function(){ $$={}; $$.data = { game:null, key :null }; $$.datas = []; $$.targetElm = null; $$.__construct = function(){ // numberplace $$.data.game = document.getElementById("game"); if($$.data.game===null){return} var table = $$.getNumberPlace(); if(table.className !== "numberplace"){return} $$.setClassName(table); $$.setInputEvent(table); // key $$.data.key = document.getElementById("key"); if(key===null){return} var keyTables = $$.data.key.getElementsByTagName("table"); if(!keyTables.length){return} $$.setKeyEvent(keyTables[0]); // Button $$.setButtonLoad(); $$.setButtonSave(); $$.setButtonProgramSave(); $$.setButtonProgramLoad(); $$.setButtonClear(); $$.setButtonFinish(); $$.setButtonSolve(); //load-sample $$.loadSample(); //$$.getCache(); }; $$.setClassName = function(table){ //cells var td = table.getElementsByTagName("td"); // set-array for(var i=0; i<td.length; i++){ td[i].setAttribute("data-num" , i); var classes = []; var row = parseInt(i/9 , 10); classes.push("row-" + row); var col = (i%9); classes.push("col-" + col); var box = Number(parseInt(i/27)*3 ,10) + Number(parseInt(parseInt(i%9 ,10)/3 ,10)); classes.push("box-" + box); td[i].className = classes.join(" "); td[i].setAttribute("data-row" , row); td[i].setAttribute("data-col" , col); td[i].setAttribute("data-box" , box); } }; // Button $$.setButtonClear = function(){ var elm = document.getElementById("clear"); if(elm===null){return} elm.onclick = function(){ $$.clearPazzle(); }; }; $$.setButtonLoad = function(){ var elm = document.getElementById("load"); if(elm===null){return} elm.onclick = function(){ //$$.setLoad(); $$.getCache(); }; }; $$.setButtonSave = function(){ var elm = document.getElementById("save"); if(elm===null){return} elm.onclick = function(){ $$.setSave(); }; }; $$.setButtonProgramSave = function(){ var elm = document.getElementById("programSave"); if(elm===null){return} elm.onclick = function(){ $$.setProgramSave(); }; }; $$.setButtonProgramLoad = function(){ var elm = document.getElementById("programLoad"); if(elm===null){return} elm.onclick = function(){ $$.setProgramLoad(); }; }; $$.clearPazzle = function(){ var table = $$.getNumberPlace(); var td = table.getElementsByTagName("td"); for(var i=0; i<td.length; i++){ td[i].innerHTML = ""; td[i].setAttribute("data-enable" , ""); } }; $$.setComplete = function(){ var table = $$.getNumberPlace(); var td = table.getElementsByTagName("td"); for(var i=0; i<td.length; i++){ td[i].setAttribute("data-enable" , "complete"); } }; $$.setSave = function(){ var table = $$.getNumberPlace(); var td = table.getElementsByTagName("td"); var data = []; var emptyFlg = 0; for(var i=0; i<td.length; i++){ var num = td[i].innerHTML; if(num === ""){ data[i] = "-"; } else{ data[i] = Number(num); } if(td[i].innerHTML!==""){emptyFlg++;} } if(emptyFlg > 0){ var str = $$.getSave_arr2str(data); localStorage.setItem("numberplace", str); } else{ localStorage.removeItem("numberplace"); } }; $$.setProgramSave = function(){ var table = $$.getNumberPlace(); var td = table.getElementsByTagName("td"); var data = []; var emptyFlg = 0; for(var i=0; i<td.length; i++){ var num = td[i].innerHTML; if(num === ""){ data[i] = "-"; } else{ data[i] = Number(num); } if(td[i].innerHTML!==""){emptyFlg++;} } if(emptyFlg > 0){ var str = $$.getSave_arr2str(data); localStorage.setItem("numberplace_program", str); } else{ localStorage.removeItem("numberplace_program"); } }; $$.setProgramLoad = function(){ var table = $$.getNumberPlace(); var td = table.getElementsByTagName("td"); var ls = localStorage.getItem("numberplace_program"); if(!ls){return} var data = $$.getSave_str2arr(ls); for(var i=0; i<td.length; i++){ //if(!data[i] || !data[i].match(/[1-9]/)){data[i] = ""} if(!data[i].toString().match(/[1-9]/)){data[i] = ""} td[i].innerHTML = data[i]; } }; $$.getCache = function(){ var table = $$.getNumberPlace(); var td = table.getElementsByTagName("td"); var ls = localStorage.getItem("numberplace"); if(!ls){return} var data = $$.getSave_str2arr(ls); for(var i=0; i<td.length; i++){ // if(data[i] === 0){data[i] = ""} // if(data[i] === "-"){data[i] = ""} if(!data[i].toString().match(/[1-9]/)){data[i] = ""} td[i].innerHTML = data[i]; } }; $$.getProgramData = function(){ var ls = localStorage.getItem("numberplace_program"); if(!ls){return ""} return $$.getSave_str2arr(ls); }; /** * LocalStorage-Save Value * condition : Numeric (1-digit) */ $$.getSave_arr2str = function(arr){ var str=""; for(var i=0; i<arr.length; i++){ str += arr[i].toString(); } return str; }; $$.getSave_str2arr = function(str){ str = str.replace(/\"/g , ''); var arr=[]; for(var i=0; i<str.length; i++){ arr.push(Number(str.charAt(i))); } return arr; }; $$.loadSample = function(){ $$LIB.ajax.set({ url:"./data/sample.json", method:"GET", async:"true", //type:"text/javascript", //query:{}, onSuccess:function(res){ if(!res){return} var json = JSON.parse(res); var lists = document.getElementById("lists"); for(var i=0; i<json.length; i++){ var li = document.createElement("li"); li.innerHTML = json[i].name; li.setAttribute("data-np" , json[i].data); li.onclick = $$.clickDataLists; lists.appendChild(li); } } }); }; $$.clickDataLists = function(event){ var elm = event.target; var data = elm.getAttribute("data-np"); if(!data){return} $$.setData_list2view(data); } $$.setData_list2view = function(data_np){ if(!data_np){return} var table = $$.getNumberPlace(); var td = table.getElementsByTagName("td"); var data = $$.getSave_str2arr(data_np); for(var i=0; i<td.length; i++){ if(data[i].toString().match(/[1-9]/)){ td[i].innerHTML = data[i]; td[i].setAttribute("data-enable","false"); } else{ td[i].innerHTML = ""; td[i].setAttribute("data-enable","true"); } } // cache localStorage.setItem("numberplace_program" , data_np); }; /** * Input */ $$.setInputEvent = function(table){ var td = table.getElementsByTagName("td"); for(var i=0; i<td.length; i++){ td[i].onclick = $$.viewKeyElement; } }; $$.viewKeyElement = function(event){ var elm = event.target; if(elm.getAttribute("data-enable") === "false"){return} $$.setCellsColorOn(elm); $$.targetElm = elm; var key = document.getElementById("key"); $$.setKeyPosition(key , elm); }; $$.setKeyPosition = function(key , elm){ var win = $$LIB.getWindowSize(); var pos = $$LIB.getPos(elm); if(pos.x < win.x/2){ key.style.setProperty("right","0",""); key.style.setProperty("left","auto",""); } else{ key.style.setProperty("right","auto",""); key.style.setProperty("left","16px",""); } key.style.setProperty("display","block",""); }; $$.setKeyEvent = function(table){ var td = table.getElementsByTagName("td"); for(var i=0; i<td.length; i++){ td[i].onclick = $$.clickKey; } }; $$.clickKey = function(event){ if($$.targetElm === null){return} //$$.cancelSolveError(); $$.cancelPazzleCheck(); var elm = event.target; var key = document.getElementById("key"); key.style.setProperty("display","none",""); $$.targetElm.innerHTML = elm.getAttribute("data-key"); $$.setCellsColorOff($$.targetElm); $$.targetElm = null; }; $$.setCellsColorOn = function(elm){ if($$.targetElm !== null){ $$.setCellsColorOff($$.targetElm); } // cross var row = elm.getAttribute("data-row"); var rows = $$.data.game.getElementsByClassName("row-"+row); for(var i=0; i<rows.length; i++){ if(rows[i].getAttribute("data-enable") === "false"){continue} if(rows[i] === elm){continue} rows[i].setAttribute("data-enable" , "active"); } var col = elm.getAttribute("data-col"); var cols = $$.data.game.getElementsByClassName("col-"+col); for(var i=0; i<cols.length; i++){ if(cols[i].getAttribute("data-enable") === "false"){continue} if(cols[i] === elm){continue} cols[i].setAttribute("data-enable" , "active"); } //target elm.setAttribute("data-enable" , "target"); }; $$.setCellsColorOff = function(elm){ //cross + target var row = elm.getAttribute("data-row"); var rows = $$.data.game.getElementsByClassName("row-"+row); for(var i=0; i<rows.length; i++){ if(rows[i].getAttribute("data-enable") === "false"){continue} if(rows[i] === elm){continue} rows[i].setAttribute("data-enable" , "true"); } var col = elm.getAttribute("data-col"); var cols = $$.data.game.getElementsByClassName("col-"+col); for(var i=0; i<cols.length; i++){ if(cols[i].getAttribute("data-enable") === "false"){continue} if(cols[i] === elm){continue} cols[i].setAttribute("data-enable" , "true"); } //target elm.setAttribute("data-enable" , "true"); }; /** * Finish */ $$.setButtonFinish = function(){ var elm = document.getElementById("finish"); if(elm===null){return} elm.onclick = function(){ if(!$$.checkPazzle()){ $$.setComplete(); //alert("complete"); } else{ //alert("not fix"); //$$.checkSolve(); } }; }; // return error-count $$.checkPazzle = function(){ var table = $$.getNumberPlace(); //flg var flg = 0; for(var i=0; i<9; i++){ //check-row var rows = table.getElementsByClassName("row-"+i); var arrRow = []; for(var j=0; j<rows.length; j++){ if(rows[i].getAttribute("data-enable") === "false"){continue} var num = rows[j].innerHTML; if(num===""){ flg++; continue; } if(arrRow.indexOf(num)!==-1){ flg++; } arrRow.push(num); } //overlap arrRow = arrRow.filter(function (x, i, self) { return self.indexOf(x) === i && i !== self.lastIndexOf(x); }); for(var j=0; j<rows.length; j++){ var val = rows[j].innerHTML; if(val === ""){ rows[j].setAttribute("data-enable" , "bad"); } else if(arrRow.indexOf(val) != -1){ rows[j].setAttribute("data-enable" , "bad"); } } //check-col var cols = table.getElementsByClassName("col-"+i); var arrCol = []; for(var j=0; j<cols.length; j++){ if(cols[i].getAttribute("data-enable") === "false"){continue} var num = cols[j].innerHTML; if(num===""){ flg++; continue; } if(arrCol.indexOf(num)!==-1){ flg++; } arrCol.push(num); } //overlap arrCol = arrCol.filter(function (x, i, self) { return self.indexOf(x) === i && i !== self.lastIndexOf(x); }); for(var j=0; j<cols.length; j++){ var val = cols[j].innerHTML; if(val === ""){ cols[j].setAttribute("data-enable" , "bad"); } else if(arrCol.indexOf(val) !== -1){ cols[j].setAttribute("data-enable" , "bad"); } } //check-box var boxs = table.getElementsByClassName("box-"+i); var arrBox = []; for(var j=0; j<boxs.length; j++){ if(boxs[i].getAttribute("data-enable") === "false"){continue} var num = boxs[j].innerHTML; if(num===""){ flg++; continue; } if(arrBox.indexOf(num)!==-1){ flg++; } arrBox.push(num); } //overlap arrBox = arrBox.filter(function (x, i, self) { return self.indexOf(x) === i && i !== self.lastIndexOf(x); }); for(var j=0; j<boxs.length; j++){ var val = boxs[j].innerHTML; if(val === ""){ boxs[j].setAttribute("data-enable" , "bad"); } else if(arrBox.indexOf(val) !== -1){ boxs[j].setAttribute("data-enable" , "bad"); } } } return flg; }; $$.cancelPazzleCheck = function(){ var table = $$.getNumberPlace(); var td = table.getElementsByTagName("td"); for(var i=0; i<td.length; i++){ if(td[i].getAttribute("data-enable")!==null){ td[i].removeAttribute("data-enable"); } } }; /** * Solve */ $$.setButtonSolve = function(){ var elm = document.getElementById("solve"); //elm.onclick = $$.solve.set; elm.onclick = $$.solve.action; }; $$.solve = { action:function(){ // Empty-Check if($$.solve.checkAllEmpty()===true){ alert("All empty !!!"); return false; } // First-set $$.solve.setDataTemp(); // Loop-Check var cnt = $$.solve.loop(); console.log("level :" + cnt ); // PazzleFinishCheck if(!$$.checkPazzle()){ $$.setComplete(); } }, // Inition-only setDataTemp:function(){ var table = $$.getNumberPlace(); var td = table.getElementsByTagName("td"); // set 9.length for(var i=0; i<td.length; i++){ if(td.innerHTML !== ""){ td[i].setAttribute("data-temp","123456789"); } } // temp-data-remove $$.solve.checkFirstAll(); }, checkAllEmpty:function(){ var table = $$.getNumberPlace(); var td = table.getElementsByTagName("td"); var empty = 0; for(var i=0; i<td.length; i++){ if(td[i].innerHTML!==""){empty++} } if(empty===0){return true}else{return false} }, loop:function(){ var num=1; console.log("start"); //$$.solve.check(); while($$.solve.check()!==0){ console.log(num); num++; } return num; }, check:function(){ $$.solve.checkFirstAll(); $$.solve.checkUniqueGroupAll(); var cnt = 0; cnt += $$.solve.checkUnique(); return cnt; }, checkFirstAll:function(){ $$.solve.checkFirst("row"); $$.solve.checkFirst("col"); $$.solve.checkFirst("box"); }, checkFirst:function(key){ var table = $$.getNumberPlace(); for(var i=0; i<9; i++){ var tempValues = $$.solve.getGroupValues(key,i); //console.log(key+"/"+i+"/"+temp); $$.solve.setTemp(key,i,tempValues); } //$$.solve.checkFirstAll(); }, checkUniqueGroupAll:function(){ $$.solve.checkUniqueGroup("row"); $$.solve.checkUniqueGroup("col"); $$.solve.checkUniqueGroup("box"); }, checkUniqueGroup:function(key){ var table = $$.getNumberPlace(); for(var cnt=0; cnt<9; cnt++){ var cells = table.getElementsByClassName(key+"-"+cnt); // Search for single numerical value.(単一数字を探す) for(var num=1; num<=9; num++){ var flg = 0; var elm = null; var targetValue = ""; for(var i=0; i<cells.length; i++){ if(cells[i].innerHTML!==""){continue} var tempValue = cells[i].getAttribute("data-temp"); if(tempValue===null){continue} if(tempValue.indexOf(num)!==-1){ flg++; elm = cells[i]; targetValue = num; } } if(flg!==1 || elm===null || targetValue===""){continue} for(var i=0; i<cells.length; i++){ if(cells[i].innerHTML!==""){continue} var tempValue = cells[i].getAttribute("data-temp"); if(tempValue===null){continue} if(cells[i]===elm){ cells[i].setAttribute("data-temp" , targetValue); } else{ var newValue = tempValue.split(targetValue).join(""); cells[i].setAttribute("data-temp" , newValue); } } } } }, checkUnique:function(){ var cnt = 0; var table = $$.getNumberPlace(); var td = table.getElementsByTagName("td"); for(var i=0; i<td.length; i++){ if(td[i].innerHTML!==""){continue} var temp = td[i].getAttribute("data-temp"); if(temp === null){continue} if(temp && temp.length === 1){ td[i].innerHTML = temp; td[i].removeAttribute("data-temp"); cnt++; } } return cnt; }, getGroupValues:function(key,num){ var table = $$.getNumberPlace(); var cells = table.getElementsByClassName(key+"-"+num); var values = ""; for(var i=0; i<cells.length; i++){ var str = cells[i].innerHTML; if(!str){continue} values += str; } values = $$.getStrUnique(values); return values; }, setTemp:function(key,num,values){ var table = $$.getNumberPlace(); var cells = table.getElementsByClassName(key+"-"+num); for(var i=0; i<cells.length; i++){ var str = cells[i].innerHTML; if(str){ $$.solve.removeElementTempData(cells[i]); } else{ $$.solve.setSharpen(cells[i],values); } } }, removeElementTempData:function(elm){ elm.removeAttribute("data-temp"); }, setSharpen:function(elm,delValue){ if(elm.innerHTML!==""){return} var currentValue = elm.getAttribute("data-temp"); if(currentValue===null){return} var newValue = ""; for(var i=0; i<currentValue.length; i++){ var char = currentValue.charAt(i); if(delValue.indexOf(char)!==-1){continue} newValue += char; } newValue = $$.getStrUnique(newValue); //console.log(newValue+"/"+currentValue+"/"+delValue); elm.setAttribute("data-temp" , newValue); } }; /** * Common */ $$.getNumberPlace = function(){ var tables = $$.data.game.getElementsByTagName("table"); return tables[0]; }; $$.getData = function(){ var table = $$.getNumberPlace(); var td = table.getElementsByTagName("td"); var data = []; for(var i=0; i<td.length; i++){ var num = td[i].innerHTML; if(num === ""){ data[i] = "-"; } else{ data[i] = Number(num); } } return $$.getSave_arr2str(data); }; $$.getStrUnique = function(str){ if(str==="" || str===null || str==="undefined" || typeof str !== "string"){return ""} var newStr=[]; for(var i=0; i<str.length; i++){ var char = str.charAt(i); if(newStr.indexOf(char)===-1){ newStr.push(char); } } //sort newStr = newStr.sort(function(a,b){ if( a < b ) return -1; if( a > b ) return 1; return 0; }); return newStr.join(""); }; $$.getArrayUnique = function(arr){ var unique = []; for(var i=0; i<arr.length; i++){ unique[i] = ""; if(arr[i]===""){continue} // currentStr var currentStr = arr[i]; var otherStr = (function(arr,num){ var str = ""; for(var i=0; i<arr.length; i++){ if(num == i){continue} str += arr[i]; } return str; })(arr,i); for(var j=0; j<currentStr.length; j++){ //var flg = 0; var char = currentStr.charAt(j); if(otherStr.indexOf(char)==-1){ unique[i] += char; } } } return unique; }; $$.getRowStr = function(lineNumber){//console.log(lineNumber); var table = $$.getNumberPlace(); var rows = table.getElementsByClassName("row-"+lineNumber); var data = ""; for(var i=0; i<rows.length; i++){ var str = rows[i].innerHTML; if(str!==""){data += str;} } return data; }; $$LIB.eventAdd(window , "load" , $$.__construct); })(); [ { "name":"sample-1", "data":"--7---2-----6-4---4----2--8-----1--52---5--1---6-8--7--8-------1--59-8-372-4-----" }, { "name":"sample-2", "data":"--1---3-----9-1--423---7-69--8-6--71---------19----------8-9--6---3---4-42-------" }, { "name":"sample-3", "data":"--2---6-----3-895-7-8--5-------56--8-7--9--2--8----5-----92----5-3----------431--" }, { "name":"sample-4", "data":"--4---3---9---867-12---9--8------725--------49---2-------5----7----6-94-2-8-1--3-" }, { "name":"sample-5", "data":"--2---5---86---------5--71-89--5---2--4-8--3-2--------4----2---7---4--8---9--826-" }, { "name":"sample-6", "data":"--7---3--8------4956------12-3-9-----7---3--6--5--1----8-1--5----4----9-71-2-----" }, { "name":"sample-7", "data":"--9---6--4--2--9------------754--------9-2-8-6------25-4----1979---3-----1--8----" }, { "name":"sample-8", "data":"--6---1--4--1-8-597--2------5-9--4-------6391-67-4-2------7---81------7----4-----" }, { "name":"sample-9", "data":"--6---5--3-1-8--699----5-1-5--6--8----8---1---6-87---------1953---496----1------2" }, { "name":"sample-10", "data":"--9---6--61-----2--5--3-1---7-5-826--62--4--------14-52---6--5-18-----9-9----5---" }, { "name":"sample-11", "data":"6--4-----5----7-9--1--8-3----27----4-5-----6-1----95----9-7--4--8-3----2-----6--3" } ]

解説

index.htmlに解凍ボタンを付け加えています。その影響でJS側でボタンを押したイベントを追加してますが、大した処理でないので、ここは解説カットします。 numberplace.jsの中の"$$.solve"という関数が今回のポイントですが、少し大きなプログラムになってしまったので、大まかなフローは下記のとおりです。
1. 解凍ボタンを押した時に処理を実行 2. 空欄に対して1~9の数字を仮登録 3. 仮登録した数字の縦、横、3x3の箱、の部分に重複のある数値を仮登録の数値から除外する 4. 縦、横、3x3箱の中で重複する数値の内値を拾って確定数値にする(この時点では仮登録するだけ) 5. 仮登録されている数値のうち、1つしかないものを確定数値として、反映させる。(この時仮登録は削除する) 6. 完了していない場合、3番に戻ってさらに処理をすすめる。
この手順で、初級レベルの解凍はできます。 もちろん、すこしレベルの高い問題になると、空欄を残してプログラムが終了してしまうので、この点は次回以降に対応する予定なので、認識しておいてください。

ポイント

解説の4番について、すこし分かりにくいので、図を用意しておきました。 分かりにくいですが、どうしてもわからない場合は、メールかコメントください。 別途解説したいと思います。

サンプル問題

横列を例にします

入れられる数値を全て書き出す

1が一つしかないので確定

これを繰り返す

上記の様な処理を繰り返し行うことで、初級レベルは空欄がどんどん埋まっていきます。

上級レベルに対しての課題

これでも解答できない問題は、もう少し別の判定を行わないといけません。 少し書いておくと、3x3マスの縦と横を1つのユニーク値として、他のブロックに対して優先順位を付けていき、不要判定を行なって、仮登録値を削除していくことで、確定数字を割り出すという内容です。 数独好きの人は、次回をお楽しみに・・・