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

2016/12/12

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

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

前回のおさらい

前回は、ナンプレの基本表示部分と、入力処理、回答を入力した後の正解チェック機能を作ってます。 基本的に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つのユニーク値として、他のブロックに対して優先順位を付けていき、不要判定を行なって、仮登録値を削除していくことで、確定数字を割り出すという内容です。 数独好きの人は、次回をお楽しみに・・・

人気の投稿

このブログを検索

ごあいさつ

このWebサイトは、独自思考で我が道を行くユゲタの少し尖った思考のTechブログです。 毎日興味がどんどん切り替わるので、テーマはマルチになっています。 もしかしたらアイデアに困っている人の助けになるかもしれません。

ブログ アーカイブ