ihanoi.js 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614
  1. /*
  2. iHanói
  3. http://www.usp.br/line
  4. Uso: localhost/ihanoi/index.html?n=3&lang=pt
  5. @TODO ainda nao implementado multi-lingua
  6. @AUTHOR Leônidas de Oliveira Brandão (coord. LInE)
  7. v0.5: 2020/11/22 (novo fundo; evita erro de disco sumir se de=para: nova msg 'msgDeParaIguais'; em "movaHaste(hi)" acresc. "if (topoDe == topoPara)...")
  8. v0.4: 2020/08/03
  9. v0.1: 2020/07/31
  10. v0: 2020/07/28
  11. */
  12. /*
  13. No arquivo HTML que carrega esse JavaScript deve existir as seguintes imagens:
  14. <img id="fundo" style="display:none;" src="img/img_fundo_hanoi.png" />
  15. <img id="haste0" style="display:none;" src="img/hasteA.png" />
  16. <img id="haste1" style="display:none;" src="img/hasteB.png" />
  17. <img id="haste2" style="display:none;" src="img/hasteC.png" />
  18. <img id="disco0" style="display:none;" src="img/disk1.png" />
  19. <img id="disco1" style="display:none;" src="img/disk2.png" />
  20. <img id="disco2" style="display:none;" src="img/disk3.png" />
  21. <img id="disco3" style="display:none;" src="img/disk4.png" />
  22. <img id="disco4" style="display:none;" src="img/disk5.png" />
  23. <img id="disco5" style="display:none;" src="img/disk6.png" />
  24. Dimensoes e posicionamento das imagens
  25. Hastes: 325 x 416
  26. # Posicao e tamanho dos discos:
  27. 6: 34, 250 294 130
  28. 5: 48, 210 267 130 +14 -40 -27 +0
  29. 4: 62, 170 240 130 +14 -40 -27 +0
  30. 3: 76, 130 213 130 +14 -40 -27 +0
  31. 2: 90, 90 186 130 +14 -40 -27 +0
  32. 1: 104, 50 159 130 +14 -40 -27 +0 (mas disk1 esta com 160x130)
  33. */
  34. console.log("iHanoi: inicio");
  35. var canvas;
  36. var context;
  37. var width = 1100;
  38. var height = 460;
  39. var posY0 = 290; // posicionamento do disco maior (depende de 'height')
  40. // Posicionamento dos discos nas hastes
  41. var matHastes = [ [ 5, 4, 3, 2, 1, 0], // haste A: pilha de discos (id discos em ordem inversa na haste); haste B e C vazias
  42. [-1, -1, -1, -1, -1, -1], // haste B vazia
  43. [-1, -1, -1, -1, -1, -1] ]; // haste C vazia
  44. var vetorMovimentos = []; // vetor para registrar todos os movimentos do aluno - definido na 'movaHaste(hi)'
  45. // Posicionamentos de coordenadas (x,y) para cada um dos 6 discos (no maximo)
  46. var posTx = [ 34, 48, 62, 76, 90, 104 ]; // posicoes x para discos: 6, 5, 4... +14
  47. var posTy = [ 240, 200, 160, 120, 80, 40 ]; // posicoes y para discos: 6, 5, 4... +40
  48. var nDiscos = 4; // Default entrar com 4 discos
  49. var contador = 0; // conta numero de movimentos
  50. var posx = [ 52, 66, 80, 94 ]; // posicoes x para discos: 6, 5, 4... +14
  51. var posy = [ 160, 120, 80, 40 ]; // posicoes y para discos: 6, 5, 4... +40
  52. var posx_HA = 20, posy_HA = 40; // posicao haste A
  53. var posx_HB = 370, posy_HB = 40; // posicao haste A
  54. var posx_HC = 720, posy_HC = 40; // posicao haste A
  55. redefineDiscos(nDiscos); // redefinir 'matHastes[][]'
  56. var topoHasteA = nDiscos-1, topoHasteB = topoHasteC = -1; // indice do disco no topo de cada haste
  57. var iHanoi = "iHanói";
  58. var LInE = "LInE-IME-USP";
  59. var isExercise = false; // se for exercicios, entao NAO permite alterar numero de discos
  60. var isAuthoring = false; // se for edicao, entao permita alterar numero de discos (sobrepoe opcao 'isExercise=true')
  61. var revendo = false; // durante revisao de movimentos, NAO deveria movimentar discos, se o fizer, entao anule revisao!
  62. //TODO Permitir internacionalizar botoes
  63. var btnReiniciar="Reiniciar", btnRever="Rever", btnCodigo="Código";
  64. var altBtnReiniciar="Reiniciar tudo, todos os discos para haste A", altBtnRever="Rever todos os movimentos realizados",
  65. altBtnCodigo="Examinar o código no formato do iHanói (extensão 'ihn')";
  66. var mensagem0 = "Clique na regiao da haste para selecionar origem, depois destino";
  67. var mensagem1_1 = "Parabéns! Você conseguiu mover todos os discos com ";
  68. var mensagem1_2 = " movimentos";
  69. var mensagem2_1 = "Não é permitido colocar disco maior sobre menor!";
  70. var mensagem2_2 = " sobre ";
  71. var mensagem3_1 = "Destino: ";
  72. var mensagem3_2 = " - Para novo movimento, clique em nova haste inicial";
  73. var msgTeste1 = "Parabéns conseguiu mover todos para B, mas lembre-se objetivo é C. Usou "; // 1
  74. var msgTeste2 = "Parabéns conseguiu mover todos para B e com mínimo de movimentos, mas objetivo é C. Usou "; // 2
  75. var msgTeste3 = "Parabéns conseguiu mover todos para C, mas não o mínimo de movimentos... Usou "; // 3
  76. var msgTeste4 = "Parabéns! Conseguiu mover todos para C e o mínimo de movimentos! Foram "; // 4
  77. var msgEhExercicio = "Não pode alterar número de discos! É um exercício com número de discos pré-fixado.";
  78. var msgReverProx = "Clique novamente no botão 'Rever' para o próximo movimento.";
  79. var msgReverFim = "Acabaram os movimentos registrados.";
  80. var msgReverPare = "Estava revendo movimentação, mas ao mover manualmente, a revisão foi finalizada!";
  81. var msgDeParaIguais = "Para mover um disco é preciso que a haste de destino seja diferente da haste de origem!";
  82. var mensagemNM = "Número de movimentos: ";
  83. var mensagem = mensagem0; // mensagem inicial
  84. // Posicionamento para mensagens
  85. var txtTx = 10, txtTy = 20; // iHanoi
  86. var txtMX = 10, txtMY = height-10; // barra de mensagens: posicao
  87. var txtLInEx = width-180, txtLInEy = 20; // LInE-IME-USP
  88. var tamNMX = 300, tamNMY = 20; // mensagem sobre num. movimentos: tamanho
  89. //1 var txtNMX = 2*325+50, txtNMY = height-10; // mensagem sobre num. movimentos: posicao
  90. var txtNMX = 120, txtNMY = 20; // mensagem sobre num. movimentos: posicao
  91. var tamX = 900, tamY = 20; // para area de mensagem
  92. // Gerenciamento de evento: primeiro ou segundo clique?
  93. var clickDe = -1, clickPara = -1; // origem e destino: -1,-1 = nada selecionado; x,-1 = selecionada origem; x,y = selecionadas ambas
  94. // Elementos graficos principais: Fundo + Haste + Discos
  95. var imgFundo = document.getElementById("fundo");
  96. var imgHastes = [ document.getElementById("haste0"), document.getElementById("haste1"), document.getElementById("haste2") ];
  97. var imgDiscos = [ document.getElementById("disco0"), document.getElementById("disco1"), document.getElementById("disco2"),
  98. document.getElementById("disco3"), document.getElementById("disco4"), document.getElementById("disco5") ];
  99. var corFundo1 = "#26508c"; // para fundo de mensagem
  100. canvas = document.createElement("canvas");
  101. context = canvas.getContext("2d");
  102. canvas.addEventListener("click", clickCanvas); //OK
  103. // Tamanho da area de trabalho iHanoi
  104. canvas.width = width; canvas.height = height;
  105. document.body.appendChild(canvas); // iniciar area para desenho "canvas"
  106. //D console.log("iHanoi: apos definir elementos graficos");
  107. // Anote tratar-se de exercicio
  108. function setExercise (valor) { // invocada em 'integration-functions.js: decodificaArquivo(strContent)'
  109. var element, i;
  110. // se for exercicios, entao NAO permite alterar numero de discos
  111. isExercise = true;
  112. if (valor) { // if defined, then is teacher, allow edit (iLM_PARAM_Authoring)
  113. isExercise = false;
  114. return; // nao altere permissoes de trocar numero de discos
  115. }
  116. //D alert("setExercise: " + valor + ", iLM_PARAM_Authoring=" + iLMparameters.iLM_PARAM_Authoring + ", isExercise=" + isExercise);
  117. var msg = "";
  118. for (i=1; i<7; i++) {
  119. element = document.getElementById("disco"+i);
  120. if (element!=null) // se for re-avaliacao NAO existe interface grafica
  121. element.disabled = true; // desabilita o botao
  122. // Apenas isso NAO impede entrar no tratamento de "clique" no botao, ver 'reiniciar(nD)'
  123. }
  124. //D
  125. console.log("setExercise: " + msg);
  126. }
  127. // Redefine numero de discos a serem carregados e os posiciona (todos) na haste A
  128. // Evento: quando "clicar" nos botoes com numero de discos (elemento id="disco"+i (i=0, 1, 2,...5)
  129. function redefineDiscos (n) {
  130. dif = 6-n;
  131. for (i=0; i<n; i++) { // >
  132. matHastes[0][i] = n-i-1;
  133. posx[i] = posTx[i+dif];
  134. posy[i] = posTy[i+dif];
  135. }
  136. for (i=n; i<6; i++) { // >
  137. matHastes[0][i] = -1;
  138. posx[i] = -1;
  139. posy[i] = -1;
  140. }
  141. //D
  142. console.log("redefineDiscos("+n+"): final");
  143. }
  144. // Inicio --- Para rever movimentos ja' realizados
  145. var reverMov = -1;
  146. var totalMov = -1;
  147. var copiaMovimentos = [];
  148. // @calledby: rever(), clickCanvas(mouseEvent)
  149. function limparRevisao () { // durante revisao de movimentos, NAO deveria movimentar discos, se o fizer, entao anule revisao!
  150. revendo = false; // nao mais revendo
  151. reverMov = -1;
  152. copiaMovimentos = [];
  153. }
  154. function rever () { // vetorMovimentos = { clickDe + " " + clickPara, ... }
  155. if (reverMov == -1) { // inicio
  156. limparRevisao();
  157. revendo = true; // inicio de revisao
  158. totalMov = vetorMovimentos.length;
  159. for (i=0; i<totalMov; i++) copiaMovimentos.push(vetorMovimentos[i]);
  160. reverMov = 0;
  161. reiniciar();
  162. mensagem = msgReverProx;
  163. desenhaMensagem();
  164. revendo = true; // durante revisao de movimentos, NAO deveria movimentar discos, se o fizer, entao anule revisao!
  165. return;
  166. }
  167. if (reverMov == totalMov) { // final
  168. mensagem = msgReverFim;
  169. desenhaMensagem();
  170. totalMov = reverMov = -1; // pode rever novamente
  171. clickDe = clickPara = -1;
  172. return;
  173. }
  174. var para, copia = copiaMovimentos[reverMov];
  175. itens = copiaMovimentos[reverMov++].split(' ');
  176. if (itens.length == 3) { clickDe = eval(itens[0]); para = eval(itens[2]); }
  177. else { clickDe = eval(itens[0]); para = eval(itens[1]); }
  178. // alert(itens + ": " + itens.length + ": rever: (" + copia + "): " + clickDe + "-" + clickPara);
  179. console.log(itens + ", rever: (" + copia + "): " + clickDe + " + " + clickPara + " + " + para); // itens
  180. movaHaste(para); // 'clickPara' tem que estar com -1 para completar movimento
  181. mensagem = msgReverProx; // clique novamente no 'Rever'
  182. desenhaTudo();
  183. console.log("rever(): final");
  184. } // rever()
  185. // Fim --- Para rever movimentos ja' realizados
  186. // Reiniciar o "jogo": zerar movimentos, colocar todos os discos sobre haste A
  187. function reiniciar (nD) {
  188. vetorMovimentos = []; // zerar movimentos
  189. if (nD!="" && nD!=undefined) {
  190. var element = document.getElementById("disco1");
  191. if (element.disabled) { // verifica se botao esta' desabilitado (neste caso e' exercicio)
  192. console.log("Nao pode alterar numero de discos!");
  193. mensagem = msgEhExercicio;
  194. desenhaMensagem();
  195. return;
  196. }
  197. redefineDiscos(nD);
  198. nDiscos = nD;
  199. }
  200. topoHasteA = nDiscos-1;
  201. topoHasteB = topoHasteC = -1;
  202. for (i=0; i<nDiscos; i++) { // >
  203. matHastes[1][i] = -1;
  204. matHastes[2][i] = -1;
  205. }
  206. contador = 0;
  207. redefineDiscos(nDiscos);
  208. mensagem = mensagem0;
  209. desenhaTudo();
  210. console.log("reiniciar(nD): final");
  211. }
  212. // Decompor parametros recebidos via GET: ?lang=pt&n=4
  213. // Devolve vetor: { 4, "pt" } nesta ordem
  214. function analisa_parametros_url (strParametros) {
  215. var vars = strParametros.split("&");
  216. var vetorParametros = [ 3, "pt" ]; // por padrao devolve { 3, "pt" }
  217. var msg = ""; //D
  218. var pair, key, value;
  219. //?par1=val1&par2=val2&
  220. for (var i = 0; i < vars.length; i++) { // >
  221. pair = vars[i].split("=");
  222. if (pair == "") break;
  223. key = decodeURIComponent(pair[0]);
  224. value = decodeURIComponent(pair[1]);
  225. if (key=="n") {
  226. vetorParametros[0] = value; // vetorParametros[i].push(decodeURIComponent(value));
  227. nDiscos = value; // redefine 'nDiscos'
  228. redefineDiscos(nDiscos);
  229. }
  230. else
  231. if (key=="lang")
  232. vetorParametros[1] = value; // vetorParametros[i].push(decodeURIComponent(value));
  233. msg += "("+key+","+value+") "; //D
  234. }
  235. //D console.log(vetorParametros); console.log("msg="+msg);
  236. return vetorParametros;
  237. }
  238. // Pegar parametros via GET
  239. function listaURL () {
  240. // window.location. [ href | protocol | host | hostname | port | pathname | search | hash
  241. parametros = window.location.search;
  242. if (parametros=="undefined")
  243. return;
  244. if (parametros.length>0) // >
  245. parametros = parametros.substring(1); // elimina primeiro caractere '?'
  246. analisa_parametros_url(parametros);
  247. }
  248. // Para depuracao
  249. function imprimeMovimentos (hi) {
  250. var i;
  251. var msg, hA = "[", hB = "[", hC = "[";
  252. for (i=0; i<nDiscos; i++) { // >
  253. hA += matHastes[0][i] + " ";
  254. hB += matHastes[1][i] + " ";
  255. hC += matHastes[2][i] + " ";
  256. }
  257. msg = hA + "], " + hB + "], " + hC + "]";
  258. return msg;
  259. }
  260. // Pegar o valor do disco no topo da haste 'ind_haste'
  261. // Se haste vazia, devolve -1
  262. function pegaTopoHaste (ind_haste) { // pega indice do topo da haste
  263. var topo, i;
  264. //D alert("pegaTopoHaste: ind_haste=" + ind_haste + ": " + matHastes[ind_haste] + ", matHastes=" + matHastes);
  265. i=0; while (matHastes[ind_haste][i]!=-1 && i<nDiscos) i++; // >
  266. return i-1;
  267. // Para melhorar a eficiencia, poderiamos usar diretamente as variaveis que tem indice dos topos: topoHasteA, topoHasteB, topoHasteC
  268. }
  269. // Apos movimentacao de discos entre haste, acertar variaveis de topo e "clique"
  270. // Copia no topo de destino o disco do topo de origem
  271. function atualizaTopos (topoDe, topoPara) { // Tira topo "de" e insere em "para"
  272. topoPara++;
  273. matHastes[clickPara][topoPara] = matHastes[clickDe][topoDe]; // mova disco do topo de origem para topo de destino
  274. if (matHastes[clickPara][topoPara] == undefined) { console.log("atualizaTopos("+topoDe+","+topoPara+"): erro! matHastes[clickPara][topoPara] undefined"); }
  275. // Tira disco do topo de origem
  276. matHastes[clickDe][topoDe] = -1; // remova disco que estava no topo da haste de origem
  277. topoDe--;
  278. // Atualiza globais
  279. if (clickDe==0) // haste A
  280. topoHasteA = topoDe;
  281. else
  282. if (clickDe==1) // haste B
  283. topoHasteB = topoDe;
  284. else // haste C
  285. topoHasteC = topoDe;
  286. if (clickPara==0) // haste A
  287. topoHasteA = topoPara;
  288. else
  289. if (clickPara==1) // haste B
  290. topoHasteB = topoPara;
  291. else // haste C
  292. topoHasteC = topoPara;
  293. //D alert("atualizaTopos: " + clickDe + " :: " + clickPara + ": " + imprimeMovimentos(clickPara));
  294. clickDe = clickPara = -1; // comeca novamente...
  295. }
  296. // Devolve rotulo da haste de indice 'hi'
  297. function pegaHaste (hi) {
  298. if (hi==0) return "A";
  299. if (hi==1) return "B";
  300. return "C";
  301. }
  302. // Verifica se todos os discos estao na haste C
  303. // Devolve: 0=nao moveu tudo; 1=moveu tudo para haste B; 2=moveu tudo para haste B com minimo de movimentos;
  304. // 3=moveu tudo par haste C; 4=moveu tudo par haste C com minimo de movimentos
  305. function movimentoFinal (haste, num) {
  306. var topo = pegaTopoHaste(haste);
  307. if (topo == nDiscos-1) { // moveu tudo!
  308. if (haste == 2) { // moveu para haste C
  309. if (contador == (2**nDiscos)-1) { // moveu para haste C com minimo
  310. return 4;
  311. }
  312. return 3; // moveu para haste C mas nao e' minimo
  313. }
  314. if (haste == 1) { // moveu para haste B
  315. if (contador == (2**nDiscos)-1) { // moveu para haste B com minimo
  316. return 2; // msgTeste2
  317. }
  318. return 1; // moveu para haste C mas nao e' minimo
  319. }
  320. }
  321. return 0;
  322. }
  323. // Mover disco do topo da haste 'clickDe' para a haste 'hi' (sem 'clickDe' definido)
  324. function movaHaste (hi) {
  325. var strHaste = pegaHaste(hi);
  326. var de0 = clickDe, para0 = clickPara;
  327. if (clickDe==-1 && clickPara==-1) { // inicio movimento
  328. clickDe = hi;
  329. topoDe = pegaTopoHaste(clickDe); // pega disco no topo de haste
  330. if (topoDe==-1) { // nao tem discos
  331. mensagem = "Haste " + strHaste + " está vazia! Por favor, selecione haste inicial com algum disco";
  332. clickDe = clickPara = -1;
  333. desenhaMensagem();
  334. return;
  335. }
  336. mensagem = "Origem: " + strHaste + " - Agora clique na haste destino";
  337. de0 = hi;
  338. desenhaMensagem();
  339. }
  340. else
  341. if (clickDe>-1 && clickPara==-1) { // final do movimento
  342. clickPara = hi;
  343. para0 = hi;
  344. //D alert("De="+clickDe+", Para="+clickPara+", hi="+hi);
  345. topoDe = pegaTopoHaste(clickDe); // devolve indice topo de haste
  346. topoPara = pegaTopoHaste(clickPara); // devolve indice topo de haste
  347. if (clickDe == clickPara) {
  348. str_haste = pegaHaste(clickDe); // nome da haste: "A", "B" ou "C"
  349. mensagem = msgDeParaIguais + " (haste " + str_haste + ")";
  350. console.log("Erro: Tentando mover disco para a mesma haste! (haste " + str_haste + ")");
  351. clickDe = clickPara = -1; // comeca novamente...
  352. desenhaMensagem();
  353. return -1;
  354. }
  355. if (topoPara>-1 && matHastes[clickDe][topoDe]>matHastes[clickPara][topoPara]) { // disco maior sobre menor : proibido!
  356. mensagem = mensagem2_1 + " (" + matHastes[clickDe][topoDe] + mensagem2_2 + matHastes[clickPara][topoPara] + ")";
  357. //D alert("De="+clickDe+", Para="+clickPara+": "+topoDe+","+topoPara+": " + imprimeMovimentos(-1));
  358. clickDe = clickPara = -1; // comeca novamente...
  359. desenhaMensagem();
  360. return -1;
  361. }
  362. vetorMovimentos.push(clickDe + " " + clickPara);
  363. if (topoDe<0) { console.log("movaHaste("+hi+"): "+clickDe + " " + clickPara+": erro! undefined"); } //DEBUG
  364. atualizaTopos(topoDe, topoPara);
  365. contador++;
  366. // 0=nao moveu tudo; 1=moveu tudo para haste B; 2=moveu tudo para haste B com minimo de movimentos;
  367. // 3=moveu tudo par haste C; 4=moveu tudo par haste C com minimo de movimentos
  368. respostaMov = movimentoFinal(hi, contador);
  369. switch (respostaMov) {
  370. case 0: mensagem = mensagem3_1 + strHaste + mensagem3_2; break;
  371. case 1: mensagem = msgTeste1 + contador + mensagem1_2; break;
  372. case 2: mensagem = msgTeste2 + contador + mensagem1_2; break;
  373. case 3: mensagem = msgTeste3 + contador + mensagem1_2; break;
  374. case 4: mensagem = msgTeste4 + contador + mensagem1_2; break;
  375. mensagem = mensagem1_1 + contador + mensagem1_2; // Paranbens! (falta comparar com numero minimo!)
  376. }
  377. desenhaTudo();
  378. }
  379. //console.log("movaHaste(hi): final");
  380. return 1;
  381. } // movaHaste(hi)
  382. // Dispara eventos
  383. function clickCanvas (mouseEvent) {
  384. var posx = mouseEvent.offsetX, posy = mouseEvent.offsetY; // Posicao do "mouse", valores para parametros de '.drawImage(...)'
  385. if (posx>25 && posx<350 && posy>30 && posy<440) { // > clicou na haste 1
  386. resp = movaHaste(0);
  387. }
  388. else
  389. if (posx>350 && posx<690 && posy>30 && posy<440) { // > clicou na haste 2
  390. resp = movaHaste(1);
  391. }
  392. else
  393. if (posx>690 && posx<1030 && posy>30 && posy<440) { // > clicou na haste 3
  394. resp = movaHaste(2);
  395. }
  396. if (revendo) { // estava revendo movimento mas clicou em haste, entao cancele revisao!
  397. mensagem = msgReverPare; // "Estava revendo movimentação, mas ao mover manualmente, a revisão foi finalizada!"
  398. limparRevisao();
  399. desenhaMensagem(); //D sem efeito, nao 'sleep(.)' nao permite aparecer a mensagem
  400. //sleep(1600); // em 'integration-functions.js'
  401. }
  402. }
  403. // Desenha um retangulo - modelo de http://jsfiddle.net/vu7dZ/1/
  404. function roundRect (ctx, x, y, width, height, radius, fill, stroke) {
  405. if (typeof stroke == "undefined" ) { stroke = true; }
  406. if (typeof radius === "undefined") { radius = 5; }
  407. ctx.beginPath();
  408. ctx.moveTo(x + radius, y);
  409. ctx.lineTo(x + width - radius, y);
  410. ctx.quadraticCurveTo(x + width, y, x + width, y + radius);
  411. ctx.lineTo(x + width, y + height - radius);
  412. ctx.quadraticCurveTo(x + width, y + height, x + width - radius, y + height);
  413. ctx.lineTo(x + radius, y + height);
  414. ctx.quadraticCurveTo(x, y + height, x, y + height - radius);
  415. ctx.lineTo(x, y + radius);
  416. ctx.quadraticCurveTo(x, y, x + radius, y);
  417. ctx.closePath();
  418. if (stroke) { ctx.stroke(); }
  419. if (fill) { ctx.fill(); }
  420. }
  421. // Desenha os discos em cada Haste (Haste A = matHastes[0][]; Haste B = matHastes[1][]; Haste C = matHastes[2][])
  422. // Cada imagem tem 28 pixels a mais que o disco menor (dai o "(nDiscos - ind_disco-1)*14")
  423. function desenhaDiscos () { // 'context' e' global
  424. var posx, posy, i;
  425. //D console.log("desenhaDiscos(): inicio");
  426. // Haste A
  427. posy = posY0;
  428. ind_disco = matHastes[0][0];
  429. i = 0;
  430. while (ind_disco!=-1) { // enquanto ainda tem disco, nao e' o ultimo
  431. posx = 33 + (6 - ind_disco-1)*14; // para nDiscos=6 : usar 34 + ...
  432. //TODO: precisa resolver um erro/advertencia que aparece
  433. // TypeError: Argument 1 of CanvasRenderingContext2D.drawImage could not be converted to any of: HTMLImageElement, SVGImageElement, HTMLCanvasElement, HTMLVideoElement, ImageBitmap.
  434. context.drawImage(imgDiscos[ind_disco], posx, posy);
  435. posy -= 40;
  436. i++;
  437. ind_disco = matHastes[0][i];
  438. }
  439. // Haste B
  440. posy = posY0;
  441. ind_disco = matHastes[1][0];
  442. i = 0;
  443. while (ind_disco!=-1) { // enquanto ainda tem disco, nao e' o ultimo
  444. posx = 382 + (6 - ind_disco-1)*14;
  445. if (ind_disco == undefined) { console.log("desenhaDiscos(): disco 1: erro: i=" + i); return; } // alert("desenhaDiscos(): erro: i=" + i);
  446. // console.log("desenhaDiscos(): " + imprimeMovimentos(0)); // + ", " + imprimeMovimentos(1) + ", " + imprimeMovimentos(2));
  447. context.drawImage(imgDiscos[ind_disco], posx, posy);
  448. posy -= 40;
  449. i++;
  450. ind_disco = matHastes[1][i];
  451. }
  452. // Haste C
  453. posy = posY0;
  454. ind_disco = matHastes[2][0];
  455. i = 0;
  456. while (ind_disco!=-1) { // enquanto ainda tem disco, nao e' o ultimo
  457. posx = 732 + (6 - ind_disco-1)*14;
  458. context.drawImage(imgDiscos[ind_disco], posx, posy);
  459. posy -= 40;
  460. i++;
  461. ind_disco = matHastes[2][i];
  462. }
  463. //console.log("desenhaDiscos(): final");
  464. } // desenhaDiscos()
  465. // Apenas muda a mensagem informativa
  466. function desenhaMensagem () {
  467. context.font = 'bold 14px serif';
  468. context.fillStyle = "white";
  469. //context.clearRect(txtMX, txtMY-15, tamX, tamY);
  470. context.fillRect(txtMX, txtMY-15, tamX, tamY);
  471. context.fillStyle = "black"; //"white";
  472. context.fillText(" " + mensagem, txtMX, txtMY);
  473. roundRect(context, txtMX, txtMY-15, tamX, tamY);
  474. }
  475. // Redesenha tudo
  476. function desenhaTudo () {
  477. //console.log("desenhaTudo(): inicio");
  478. context.font = 'bold 20px serif';
  479. context.drawImage(imgFundo, 0, 0, width, height );
  480. context.fillStyle = "white";
  481. context.fillText(iHanoi, txtTx, txtTy); // iHanoi
  482. context.fillText(LInE, txtLInEx, txtLInEy); // LInE-IME-USP
  483. context.drawImage(imgHastes[0], posx_HA, posy_HA); // posicao haste A
  484. context.drawImage(imgHastes[1], posx_HB, posy_HB); //
  485. context.drawImage(imgHastes[2], posx_HC, posy_HC); //
  486. context.font = 'bold 14px serif';
  487. context.fillStyle = "white"; // "#26508c"; // para fundo de mensagem
  488. //context.clearRect(txtMX, txtMY-15, tamX, tamY); // Mensagens
  489. context.fillRect(txtMX, txtMY-15, tamX, tamY); // Mensagens
  490. roundRect(context, txtMX, txtMY-15, tamX, tamY); // Mensagens
  491. //context.clearRect(txtNMX, txtNMY-15, tamNMX, tamNMY); // Numero de movimentos
  492. context.fillRect(txtNMX, txtNMY-15, tamNMX, tamNMY); // Numero de movimentos
  493. roundRect(context, txtNMX, txtNMY-15, tamNMX, tamNMY); // Numero de movimentos
  494. context.fillStyle = "black"; //"white";
  495. context.fillText(" " + mensagem, txtMX, txtMY); // mensagens
  496. context.fillText(" " + mensagemNM + contador, txtNMX, txtNMY); // numero de movimentos
  497. desenhaDiscos();
  498. //console.log("desenhaTudo(): final");
  499. } // desenhaTudo()
  500. // Versao distinta para inicia - removida em favor do 'onload' no 'body'
  501. // window.addEventListener("DOMContentLoaded", function () {
  502. // //D alert("DOMContentLoaded: " + canvas.width + "," + canvas.height);
  503. // desenhaTudo();
  504. // });
  505. // -------------------- ATUALIZACAO --------------------
  506. /* Botao automatico e velocidade -> Resolve a torre de hanoi usando recursão
  507. * TODO: corrigir sleep() nao atrasa o algoritmo adequadamente, tela nao atualiza
  508. */
  509. function resolveAutomatico (n, origem, destino, aux) {
  510. if (n == 1) { // O menor disco (1) se move livremente em qualquer haste
  511. movaHaste(origem);
  512. movaHaste(destino);
  513. console.log("Moveu de " + origem + " para " + destino);
  514. return;
  515. }
  516. //resolveAutomatico(n - 1, origem, aux, destino); // Retirar discos menores do caminho
  517. console.log("Primeiro resolve com n = " + (n - 1));
  518. setTimeout(resolveAutomatico, 100 * (potencia2(n) - 1), n - 1, origem, aux, destino);
  519. sleep(100 * (potencia2(n) - 1));
  520. movaHaste(origem);
  521. movaHaste(destino); // Mover maior disco para o destino
  522. console.log("Moveu de " + origem + " para " + destino);
  523. //sleep(1000);
  524. //resolveAutomatico(n - 1, aux, destino, origem); //Mover os discos menores para cima do maior novamente
  525. console.log("Segundo resolve com n = " + (n - 1));
  526. setTimeout(resolveAutomatico, 100 * (potencia2(n) - 1), n - 1, aux, destino, origem);
  527. sleep(100 * (potencia2(n) - 1));
  528. }
  529. let mov = [];
  530. // Verifica posicao dos discos e chama a funcao recursiva
  531. function preparaAutomatico ()
  532. {
  533. reiniciar(nDiscos); //formula nao funciona caso os discos estejam desorganizados
  534. resolveAutomatico(nDiscos, 0, 2 ,1);
  535. console.log(mov);
  536. }
  537. // -------------------- FIM DA ATUALIZACAO --------------------
  538. console.log("iHanoi: final do JavaScript principal"); //D