boulier.js 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246
  1. class Boulier {
  2. static instance = null;
  3. static colors = {
  4. red: "Rouge",
  5. pink: "Rose",
  6. purple: "Violet",
  7. indigo: "Indigo",
  8. blue: "Bleu",
  9. cyan: "Cyan",
  10. green: "Vert",
  11. lime: "Lime",
  12. yellow: "Jaune",
  13. amber: "Ambre",
  14. orange: "Orange"
  15. };
  16. static setup(elt, nbLine, nbBoule) {
  17. Boulier.instance = new Boulier(elt, nbLine, nbBoule);
  18. return Boulier.instance;
  19. }
  20. static get(elt, nbLine, nbBoule) {
  21. if (Boulier.instance != null) {
  22. return Boulier.instance;
  23. } else if (elt != null && nbLine != null && nbBoule != null) {
  24. return Boulier.setup(elt, nbLine, nbBoule);
  25. } else {
  26. return false;
  27. }
  28. }
  29. static isSaved() {
  30. return localStorage.getItem('boulier-ball') != null;
  31. }
  32. static load(elt) {
  33. if (Boulier.instance == null) {
  34. Boulier.setup(elt, 1, 1);
  35. }
  36. const instance = Boulier.get();
  37. instance.load();
  38. return instance;
  39. }
  40. static clear() {
  41. localStorage.clear();
  42. }
  43. constructor(elt, nbLine, nbBall) {
  44. const emptySpace = Math.floor(nbBall / 2);
  45. this._elt = elt;
  46. this._callback = null;
  47. this._ball = nbBall;
  48. this.boulier = [];
  49. this.color = [];
  50. for (let i = 0; i < nbLine; i++) {
  51. const line = []
  52. for (let j = 0; j < nbBall + emptySpace; j++) {
  53. line.push(j < nbBall);
  54. }
  55. this.boulier.push(line);
  56. this.color.push(['red']);
  57. }
  58. }
  59. save() {
  60. localStorage.setItem('boulier-data', JSON.stringify(this.boulier));
  61. localStorage.setItem('boulier-color', JSON.stringify(this.color));
  62. localStorage.setItem('boulier-ball', this._ball);
  63. }
  64. load() {
  65. this.boulier = JSON.parse(localStorage.getItem('boulier-data'));
  66. this.color = JSON.parse(localStorage.getItem('boulier-color'));
  67. this._ball = parseInt(localStorage.getItem('boulier-ball'));
  68. }
  69. move(line, col) {
  70. // Vérifie les paramètres
  71. if (this.boulier.length <= line || this.boulier[line].length <= col || ! this.boulier[line][col]) {
  72. return false;
  73. }
  74. let data = this.boulier[line];
  75. // Détermine s'il faut bouger les boules vers la gauche ou la droite
  76. let right = true;
  77. for (let i = col; i < data.length; i++) {
  78. if (!data[i]) {
  79. right = false;
  80. break;
  81. }
  82. }
  83. // Si les mouvement est vers la droite, on inverse la ligne
  84. if (right) {
  85. data = data.reverse();
  86. col = data.length - col - 1;
  87. }
  88. // Compte le nombre de boule à deplacer et l'index de l'endroit où faire le déplacement
  89. let cpt = 0;
  90. let gap = false;
  91. let index = null;
  92. for (let i = col; i < data.length; i++) {
  93. if (!gap && data[i]) {
  94. cpt++;
  95. data[i] = false;
  96. } else if (gap && data[i]) {
  97. index = i - 1;
  98. break;
  99. } else {
  100. gap = true;
  101. }
  102. }
  103. if (index == null) {
  104. index = data.length - 1;
  105. }
  106. // Déplace les boules
  107. for (let i = 0; i < cpt; i++) {
  108. data[index - i] = true;
  109. }
  110. // Si le mouvement était vers la droite, on remet la ligne dans le bon sens
  111. if (right) {
  112. data = data.reverse();
  113. }
  114. // Callback
  115. if (this._callback != null && typeof this._callback === 'function') {
  116. this._callback(Boulier.instance);
  117. }
  118. }
  119. reset() {
  120. for(let i = 0; i < this.boulier.length; i++) {
  121. this.move(i, this.boulier[i].length - 1);
  122. }
  123. }
  124. render() {
  125. let html = '';
  126. for (let i = 0; i < this.boulier.length; i++) {
  127. html += '<div class="grid">';
  128. // Boule
  129. let cptBall = 0;
  130. let changeColor = Math.round(this._ball / this.color[i].length);
  131. for(let j = 0; j < this.boulier[i].length; j++) {
  132. const elt = this.boulier[i][j];
  133. html += '<div class="cell"><div class="line" data-line="' + i + '" data-col="' + j + '" ondragover="allowDrop(event)" ondrop="ballDrop(event, this)""></div>';
  134. if (elt) {
  135. let ballColor = this.color[i][Math.trunc(cptBall/changeColor)];
  136. if (ballColor == null) {
  137. ballColor = this.color[i][this.color[i].length - 1];
  138. }
  139. html += '<div class="ball ' + ballColor + '" data-line="' + i + '" data-col="' + j + '" draggable="true" ondragstart="ballDrag(event)" onclick="ballClick(this)"></div>';
  140. cptBall++;
  141. }
  142. html += '</div>';
  143. }
  144. html += '</div>';
  145. }
  146. this._elt.innerHTML = html;
  147. }
  148. onChange(callback) {
  149. this._callback = callback;
  150. }
  151. tostring() {
  152. let str = '';
  153. for (const line of this.boulier) {
  154. str += '-|';
  155. for (const boule of line) {
  156. if (boule) {
  157. str += 'o';
  158. } else {
  159. str += '-';
  160. }
  161. }
  162. str += '|-\n';
  163. }
  164. return str;
  165. }
  166. print() {
  167. console.log(this.tostring());
  168. }
  169. }
  170. function ballClick(elt) {
  171. const instance = Boulier.instance;
  172. if (instance == null) {
  173. return;
  174. }
  175. const line = parseInt(elt.getAttribute('data-line'));
  176. const col = parseInt(elt.getAttribute('data-col'));
  177. instance.move(line, col);
  178. instance.render();
  179. }
  180. function allowDrop(evt) {
  181. evt.preventDefault();
  182. }
  183. function ballDrag(evt) {
  184. const line = parseInt(evt.target.getAttribute('data-line'));
  185. const col = parseInt(evt.target.getAttribute('data-col'));
  186. evt.dataTransfer.setData("text", line + ";" + col);
  187. }
  188. function ballDrop(evt, elt) {
  189. evt.preventDefault();
  190. // Récupère l'instance
  191. const instance = Boulier.instance;
  192. if (instance == null) {
  193. return;
  194. }
  195. // Recupère les infos de la balle et de la destination
  196. const line = parseInt(elt.getAttribute('data-line'));
  197. const col = parseInt(elt.getAttribute('data-col'));
  198. const split = evt.dataTransfer.getData("text").split(';');
  199. const ballLine = parseInt(split[0]);
  200. const ballCol = parseInt([split[1]]);
  201. // Vérifie si le cas est valide
  202. if (line != ballLine || col == ballCol) {
  203. return;
  204. }
  205. let right = true;
  206. for (let i = ballCol; i < instance.boulier[line].length; i++) {
  207. if (!instance.boulier[line][i]) {
  208. right = false;
  209. break;
  210. }
  211. }
  212. if ((right && col > ballCol) || (!right && col < ballCol)) {
  213. return;
  214. }
  215. // Bouge
  216. instance.move(ballLine, ballCol);
  217. instance.render();
  218. }