質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

Q&A

解決済

4回答

1073閲覧

javascript:スライドパズルゲームのリセット時にパズルがシャッフルされない

k373

総合スコア17

JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

0グッド

0クリップ

投稿2019/07/19 12:50

編集2019/07/19 13:44

■解決したいこと
16マスのスライドパズルゲームを勉強で作っています。
body onloadで、パズルがランダムにシャッフルされるようにinit()関数内に書いています。
読み込み時、問題なくシャッフルされます。

しかし、設置したresetボタンに同じinit関数をonclickで呼び出せるようにし、呼び出すとなぜかパズルがシャッフルされません。
綺麗に1から15までの数字が順番に並んでしまいます。
リセット時もシャッフル機能を問題なく動作させたいです。

javascript

1<!DOCTYPE html> 2<html lang="ja" dir="ltr"> 3 <head> 4 <meta charset="utf-8"> 5 <title>16 puzzles</title> 6 <style> 7 .tile{ 8 width:100px; 9 height:100px; 10 border:1px solid #ccc; 11 border-radius:10px; 12 text-align:center; 13 font-size:36px; 14 background-color:white; 15 box-shadow:rgb(128, 128, 128) 5px 5px; 16 } 17 .over{ 18 pointer-events: none; 19 background-color: #ff0000; 20 color:#fff; 21 } 22 </style> 23 </head> 24 <body onload="init()"> 25 <div id="score">0</div> 26 <div id="timer"></div> 27 <table id="table"></table> 28 <div id="reset" onclick="init()">RESET</div> 29 <script> 30 "use strict" 31 32 var tiles = []; 33 34 var timer = document.getElementById("timer"); 35 var score = document.getElementById("score"); 36 var reset = document.getElementById("reset"); 37 var table = document.getElementById("table"); 38 var timeoutId; 39 var time = 180; 40 var min = 0; 41 var sec = 0; 42 var clickcount = 0; 43 44 45 function init(){ 46 table.innerHTML = ""; 47 48 timer.textContent = "03:00" 49 50 for(var i = 0; i < 4; i ++){ 51 var tr = document.createElement("tr"); 52 table.appendChild(tr); 53 for(var j = 0; j < 4; j ++){ 54 var td = document.createElement("td"); 55 var index = i * 4 + j; 56 td.className = "tile"; 57 td.value = index; 58 td.index = index; 59 td.textContent = index == 0 ? "" : index; //indexが0なら空でその他はその数字を代入 60 tiles.push(td); 61 tr.appendChild(td); 62 td.onclick = click; 63 } 64 } 65 for(var i = 0; i < 1000; i++){ 66 clickrandom({srcElement: {index: Math.floor(Math.random() * 16)}}) //e.srcElement.indexのように、srcElementプロパティのindexプロパティ 67 } 68 } 69 70 71 function countdown(){ 72 if(time > 0){ 73 var min = Math.floor(time/60); 74 var sec = time % 60; 75 time--; 76 console.log(time); 77 timeoutId = setTimeout(countdown, 1000); 78 timer.innerHTML = `${String(min).padStart(2, '0')}:${String(sec).padStart(2, '0')}`; 79 finish(); 80 }else{ 81 timer.innerHTML = "TIME UP!"; 82 scoredata(); 83 clearTimeout(timeoutId); 84 var tileElem = document.getElementsByClassName("tile") 85 Array.prototype.forEach.call(tileElem, function(element){ 86 element.classList.add("over"); 87 }); 88 } 89 } 90 91 var first = true;//startTimerの外でtrueにしないとダメ。中だと都度呼び出されてしまう。 92 function startTimer(){ 93 if(first){ 94 countdown(); 95 } 96 first = false; 97 } 98 99 function clickrandom(e){ 100 var i = e.srcElement.index; 101 102 if(i - 4 >= 0 && tiles[i - 4].value == 0){ //一番上の行ではなく、上にあるマスの値が0の時 103 swap(i, i - 4); 104 }else if(i + 4 < 16 && tiles[i + 4].value == 0){ //一番下の行ではなく、下にあるマスの値が0の時 105 swap(i, i + 4); 106 }else if(i % 4 != 0 && tiles[i - 1].value == 0){ //一番下の行ではなく、下にあるマスの値が0の時 107 swap(i, i - 1); 108 }else if(i % 4 != 3 && tiles[i + 1].value == 0){ //一番下の行ではなく、下にあるマスの値が0の時 109 swap(i, i + 1); 110 } 111 112 } 113 114 function click(e){ 115 var i = e.srcElement.index; 116 117 if(i - 4 >= 0 && tiles[i - 4].value == 0){ //一番上の行ではなく、上にあるマスの値が0の時 118 swap(i, i - 4); 119 startTimer(); 120 clickcount++; 121 }else if(i + 4 < 16 && tiles[i + 4].value == 0){ //一番下の行ではなく、下にあるマスの値が0の時 122 swap(i, i + 4); 123 startTimer(); 124 clickcount++; 125 }else if(i % 4 != 0 && tiles[i - 1].value == 0){ //一番下の行ではなく、下にあるマスの値が0の時 126 swap(i, i - 1); 127 startTimer(); 128 clickcount++; 129 }else if(i % 4 != 3 && tiles[i + 1].value == 0){ //一番下の行ではなく、下にあるマスの値が0の時 130 swap(i, i + 1); 131 startTimer(); 132 clickcount++; 133 } 134 } 135 136 function swap(i,j){ 137 var tmp = tiles[i].value; //先にiの値をtmpに代入 138 tiles[i].textContent = tiles[j].textContent; //iにjを代入 139 tiles[i].value = tiles[j].value; //iにjを代入 140 tiles[j].textContent = tmp; //jにiを代入 141 tiles[j].value = tmp; //jにiを代入 142 } 143 144 function scoredata(){ 145 var scoreCount = time * 100 + 200 - clickcount * 10; 146 score.textContent = scoreCount; 147 } 148 149 function finish(){//クリア判定 150 var clear = 0; 151 for(var i = 0; i < 16; i++){//16枚のタイルを判定 152 if(tiles[i].value == i){ 153 clear++//タイルの場所とその数字が同じ場合はポイント追加 154 } 155 } 156 console.log(clear); 157 if(clear == 16){//ポイントが16(つまり全部一致)になったらクリア! 158 clearTimeout(timeoutId);  159 timer.innerHTML = "FINISH!"; 160 scoredata(); 161 } 162 } 163 164 </script> 165 </body> 166</html> 167

ご教授よろしくお願いいたします。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答4

0

init() 実行時に tiles の中身をリセットしていないからですね。

投稿2019/07/19 13:59

R.Mizukami

総合スコア1086

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

k373

2019/07/19 14:10

ご回答ありがとうございます。結果は解決しました。ただ、どのようにすればその問題を発見できたのか、アドバイスをいただけますと幸いです。
R.Mizukami

2019/07/19 16:18

一度目は動くけれども2度目以降でダメ、ということは、「追加していく系」の処理に不具合がある可能性が高いだろうと思いました。(一度目はゼロから追加していくから想定通りの形になるが、2度目以降は既にあるデータの後ろに追加されてしまってバグるパターンはよくあるので) そこまでアタリをつけられたら、疑わしいのは appendChild と push くらいなので、そのあたりを確認すればすぐにわかりました。 この手のミスはよくやるので、私なら初期化処理と更新処理は分離し、 onload では初期化→更新を呼ぶ、 reset では 更新を呼ぶ という設計にすると思います。
k373

2019/07/23 07:52

アドバイスありがとうございます。 問題を推測するプロセス、非常にわかりやすいです。 また、初期化と更新の機能を分離して設計する点、勉強になります。 今後は機能を整理して設計することを心がけます。 ありがとうございました!
guest

0

table.appendChild(tr);

2回目のinitの実行の際には、tableって何が入ってるのか考えてみよう

投稿2019/07/19 13:53

y_waiwai

総合スコア87749

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

k373

2019/07/19 14:09

2回目のinit実行時、何がtableに入っているのか、consoleで色々と試してみましたが、1回目の異なる結果を得ることはできませんでした。 tilesの中身が入ったまま追加されていることが原因だということは、上記のご回答のお陰でわかりましたが、consoleを使用してその問題にどうたどり着けるのかはまだ不明です。 アドバイスがあればいただけますと幸いです。
y_waiwai

2019/07/19 14:16

> table.innerHTML = ""; 見落としてましたね、スンマセン マイナスしといてください
k373

2019/07/19 14:22

いえいえ。ご回答いただけただけで助かります。今後ともよろしくお願いいたします。 いつもありがとうございます。
guest

0

不要な /がコード内にある、td.onclick = click;のclickが未定義など
プログラムとして成立していないです。

質問前にJavaScriptコードを書いたあとは
Consoleでエラーのチェックは必ず行ってください。

投稿2019/07/19 13:42

yasutomi

総合スコア2937

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

k373

2019/07/19 13:45

すみません。コードの一部しか載せておりませんでした。 全体像に変更しました。 consoleでのエラーは確認済みです。 よろしくお願いいたします。
guest

0

ベストアンサー

tilesをリセットすれば確かに直りますが、
そもそもtable内のタグ部分は1回生成すれば良いので
解決方法としてはこのようにcreateElementはonload時だけ
行った方が無駄な処理がなくなるので、より良い解決方法になります。

html

1<body onload="init()"> 2<style> 3.tile { 4 width: 100px; 5 height: 100px; 6 border: 1px solid #ccc; 7 border-radius: 10px; 8 text-align: center; 9 font-size: 36px; 10 background-color: white; 11 box-shadow: rgb(128, 128, 128) 5px 5px; 12} 13</style> 14<table id="table"></table> 15<div id="reset">RESET</div> 16<script> 17"use strict"; 18var tiles = []; 19var reset = document.getElementById("reset"); 20var table = document.getElementById("table"); 21var isInit = false; 22 23function init() { 24 if (!isInit) { 25 table.innerHTML = ""; 26 for (var i = 0; i < 4; i++) { 27 var tr = document.createElement("tr"); 28 table.appendChild(tr); 29 for (var j = 0; j < 4; j++) { 30 var td = document.createElement("td"); 31 var index = i * 4 + j; 32 td.className = "tile"; 33 td.value = index; 34 td.index = index; 35 td.textContent = index == 0 ? "" : index; 36 tiles.push(td); 37 tr.appendChild(td); 38 td.addEventListener("click", clickNum); 39 } 40 isInit = true; 41 } 42 } 43 for (var i = 0; i < 1000; i++) { 44 clickrandom({ 45 srcElement: { 46 index: Math.floor(Math.random() * 16) 47 } 48 }); 49 } 50} 51reset.addEventListener("click", init); 52 53function clickrandom(e) { 54 var i = e.srcElement.index; 55 if (i - 4 >= 0 && tiles[i - 4].value == 0) { 56 swap(i, i - 4); 57 } else if (i + 4 < 16 && tiles[i + 4].value == 0) { 58 swap(i, i + 4); 59 } else if (i % 4 != 0 && tiles[i - 1].value == 0) { 60 swap(i, i - 1); 61 } else if (i % 4 != 3 && tiles[i + 1].value == 0) { 62 swap(i, i + 1); 63 } 64} 65 66function clickNum(e) { 67 var i = e.srcElement.index; 68 if (i - 4 >= 0 && tiles[i - 4].value == 0) { 69 swap(i, i - 4); 70 clickcount++; 71 } else if (i + 4 < 16 && tiles[i + 4].value == 0) { 72 swap(i, i + 4); 73 clickcount++; 74 } else if (i % 4 != 0 && tiles[i - 1].value == 0) { 75 swap(i, i - 1); 76 clickcount++; 77 } else if (i % 4 != 3 && tiles[i + 1].value == 0) { 78 swap(i, i + 1); 79 clickcount++; 80 } 81} 82function swap(i, j) { 83 var tmp = tiles[i].value; //先にiの値をtmpに代入 84 tiles[i].textContent = tiles[j].textContent; //iにjを代入 85 tiles[i].value = tiles[j].value; //iにjを代入 86 tiles[j].textContent = tmp; //jにiを代入 87 tiles[j].value = tmp; //jにiを代入 88} 89</script> 90</body>

投稿2019/07/19 14:55

yasutomi

総合スコア2937

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

k373

2019/07/19 15:02

簡潔な解決方法とコードの整理ありがとうございます!! 確かに重複処理がなくなり、綺麗です。 自分のコードと比較して、勉強します。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問