diff --git a/frontend/mvc/controller/Controller.js b/frontend/mvc/controller/Controller.js index 142e698..924413b 100644 --- a/frontend/mvc/controller/Controller.js +++ b/frontend/mvc/controller/Controller.js @@ -2,14 +2,6 @@ import Board from '../model/Board'; import Painter from '../view/Painter'; -const autobind = (self) => { - Object.getOwnPropertyNames(self.constructor.prototype).forEach((key) => { - const val = self[key]; - if (key !== 'constructor' && typeof val === 'function') self[key] = val.bind(self); - }); - return self; -}; - class Controller { constructor() { this.model = new Board(10, 10); @@ -17,7 +9,7 @@ class Controller { this.running = false; this.fps = 1; this.setSubscription(); - this.view.newTable(this.model.matrix);// начальная отрисовка + this.view.initTable(this.model.matrix);// начальная отрисовка this.setRunning(false); } setSubscription() { @@ -34,7 +26,6 @@ class Controller { toggleCell({ target }) { const cell = target.cellIndex; const row = target.parentElement.sectionRowIndex; - this.view.toggleCell(target); this.model.toggleCell(row, cell); } setRunning(value) { @@ -44,20 +35,21 @@ class Controller { } anim(callback) { // останавливается и вызывет аргумент callback(для тестов), когда матрица перестает меняться - function loop() { + const self = this; + const loop = function loop() { setTimeout(() => { - if (this.running) { - requestAnimationFrame(loop.bind(this)); - const flag = this.model.worker(); - if (!flag) { // изменилась ли матрица ? - this.setRunning(false); + if (self.running) { + requestAnimationFrame(loop); + const flag = self.model.calculateMatrix(); + if (flag) { // повторилась ли матрица ? + self.setRunning(false); } } else if (callback) { callback(); } - }, 1000 / this.fps); - } - loop.call(this); + }, 1000 / self.fps); + }; + loop(); } handlerButtons({ target }) { switch (target.innerHTML) { @@ -69,7 +61,7 @@ class Controller { this.setRunning(false); break; case 'clear': - this.model.clear(); + this.model.clearMatrix(); this.setRunning(false); } } @@ -81,11 +73,11 @@ class Controller { break; case 'width': this.setRunning(false); - this.model.resize(this.model.rows, value); + this.model.resizeMatrix(this.model.rows, value); break; case 'height': this.setRunning(false); - this.model.resize(value, this.model.columns); + this.model.resizeMatrix(value, this.model.columns); } } } diff --git a/frontend/mvc/model/Board.js b/frontend/mvc/model/Board.js index 29580ab..2342f3d 100644 --- a/frontend/mvc/model/Board.js +++ b/frontend/mvc/model/Board.js @@ -1,141 +1,80 @@ -class Event { - constructor(sender) { - this.sender = sender; - this.listeners = []; - } - attach(listener) { - this.listeners.push(listener); - } - notify(args) { - this.listeners.forEach((listener) => { - listener(this.sender, args); - }); - } -} +import Event from '../utils/Event'; class Board { constructor(rows = 10, columns = 10) { - // матрица m на n заполненная false - this.matrix = []; + this.initMatrix(rows, columns); this.rows = rows; this.columns = columns; this.listOldMatrix = []; this.matrixChanged = new Event(this); - this.cellToggled = new Event(this); + } + initMatrix(rows, columns) { + this.matrix = []; for (let i = 0; i < rows; i += 1) { - const row = []; + let row = []; for (let j = 0; j < columns; j += 1) { row.push(false); } this.matrix.push(row); } } - resize(m, n) { - const { matrix } = this; - const o = matrix.length; - const p = matrix[0].length; - // убираем столбцы - if (p > n) { - for (let i = 0; i < o; i += 1) { - matrix[i].splice(n - 1, p - n);// изменить length? - } - } - - // добавляем столбцы - if (p < n) { - for (let i = 0; i < o; i += 1) { - for (let j = p; j < n; j += 1) { - matrix[i].push(false); - } - } - } - - // убираем строки - if (o > m) matrix.splice(m - 1, o - m);// изменить length? - - // добавляем строки - if (o < m) { - const line = []; - for (let j = 0; j < n; j += 1) { - line.push(false); - } - - for (let i = o; i < m; i += 1) { - matrix.push(line.slice()); - } - } - - this.rows = m; - this.columns = n; + resizeMatrix(rows, columns) { + this.initMatrix(rows, columns); + this.rows = rows; + this.columns = columns; this.listOldMatrix = []; this.matrixChanged.notify({ matrix: this.matrix, resized: true }); } - clear() { - this.matrix.forEach((row) => { - row.forEach((item, i, arr) => { - arr[i] = false; - }); - }); + clearMatrix() { + this.initMatrix(this.rows, this.columns); this.listOldMatrix = []; - this.matrixChanged.notify({ matrix: this.matrix, clear: true }); + this.matrixChanged.notify({ matrix: this.matrix }); } - worker() { + calculateMatrix() { // обход всех ячеек с записью нового состояния - const newMatrix = []; - let flag = false;// изменилась ли матрица? - this.matrix.forEach((row, i) => { - const newRow = []; - row.forEach((cell, j) => { - const newCell = this.calculateCell(i, j); - newRow.push(newCell); - if (newCell !== cell) flag = true; - }); - newMatrix.push(newRow); - }); - - if (flag) { // изменилась ли матрица в сравнении с предыдущей ? - this.matrix = newMatrix; - flag = !this.isRepeat(newMatrix); // а в сравнении со всеми предыдущими ? - } - this.matrixChanged.notify({ matrix: this.matrix, isChanged: !flag }); + const newMatrix = this.matrix.map((row, i) => row.map((cell, j) => this.calculateCell(i, j))); + const flag = this.isRepeatMatrix(newMatrix); // повторилась ли матрица? + this.matrixChanged.notify({ matrix: this.matrix }); + this.matrix = newMatrix; return flag; } - isRepeat(newMatrix) { + isRepeatMatrix(newMatrix) { const flag = this.listOldMatrix.some((matrix) => { if (matrix.length !== newMatrix.length) return false; if (matrix[0].length !== newMatrix[0].length) return false; return matrix.every((row, i) => row.every((cell, j) => (cell === newMatrix[i][j]))); }); - this.listOldMatrix.push(newMatrix); + if (flag) this.listOldMatrix = []; + else this.listOldMatrix.push(newMatrix); return flag; } - calculateCell(i, j) { + calculateCell(row, column) { // соседи за пределами поля считаются мертвыми let count = 0;// живые соседи - let newCell = this.matrix[i][j]; + let newCell = this.matrix[row][column]; - if (this.matrix[i - 1]) { - if (this.matrix[i - 1][j - 1]) count += 1; - if (this.matrix[i - 1][j]) count += 1; - if (this.matrix[i - 1][j + 1]) count += 1; + if (this.matrix[row - 1]) { + if (this.matrix[row - 1][column - 1]) count += 1; + if (this.matrix[row - 1][column]) count += 1; + if (this.matrix[row - 1][column + 1]) count += 1; } - if (this.matrix[i][j - 1]) count += 1; - if (this.matrix[i][j + 1]) count += 1; + if (this.matrix[row][column - 1]) count += 1; + if (this.matrix[row][column + 1]) count += 1; - if (this.matrix[i + 1]) { - if (this.matrix[i + 1][j - 1]) count += 1; - if (this.matrix[i + 1][j]) count += 1; - if (this.matrix[i + 1][j + 1]) count += 1; + if (this.matrix[row + 1]) { + if (this.matrix[row + 1][column - 1]) count += 1; + if (this.matrix[row + 1][column]) count += 1; + if (this.matrix[row + 1][column + 1]) count += 1; } if (count < 2 || count > 3) newCell = false; else if (count === 3) newCell = true; return newCell; } - toggleCell(i, j) { - this.matrix[i][j] = !this.matrix[i][j]; - this.cellToggled.notify(i, j); + toggleCell(row, column) { + this.matrix[row][column] = !this.matrix[row][column]; + this.matrixChanged.notify({ matrix: this.matrix }); } } export default Board; diff --git a/frontend/mvc/utils/Event.js b/frontend/mvc/utils/Event.js new file mode 100644 index 0000000..a111d62 --- /dev/null +++ b/frontend/mvc/utils/Event.js @@ -0,0 +1,15 @@ +class Event { + constructor(sender) { + this.sender = sender; + this.listeners = []; + } + attach(listener) { + this.listeners.push(listener); + } + notify(args) { + this.listeners.forEach((listener) => { + listener(this.sender, args); + }); + } +} +export default Event; diff --git a/frontend/mvc/view/Painter.js b/frontend/mvc/view/Painter.js index 06f97a5..0769563 100644 --- a/frontend/mvc/view/Painter.js +++ b/frontend/mvc/view/Painter.js @@ -1,31 +1,24 @@ -class Event { - constructor(sender) { - this.sender = sender; - this.listeners = []; - } - attach(listener) { - this.listeners.push(listener); - } - notify(args) { - this.listeners.forEach((listener) => { - listener(this.sender, args); - }); - } -} +import Event from '../utils/Event'; class Painter { constructor(model) { this.model = model; + this.initDOMElements(); + this.initEvents(); + this.initHandlers(); + this.initSubscription(); + } + initDOMElements() { this.table = document.getElementById('board'); this.controls = document.getElementById('controls'); this.buttons = this.controls.querySelectorAll('button'); + } + initEvents() { this.tableClicked = new Event(this); this.buttonClicked = new Event(this); this.sliderChanged = new Event(this); - this.setHandlers(); - this.setSubscription(); } - setHandlers() { + initHandlers() { this.table.onclick = (event) => { if (event.target.tagName === 'TD') { this.tableClicked.notify(event); @@ -42,22 +35,20 @@ class Painter { } }; } - setSubscription() { + initSubscription() { this.model.matrixChanged.attach((sender, obj) => { - if (obj.resized) this.newTable(obj.matrix); - else this.repaintTable(obj.matrix); + if (obj.resized) this.initTable(obj.matrix); + else this.changeTable(obj.matrix); }); } setButtons(running) { if (!this.buttons) return; this.buttons.forEach((button) => { if (button.innerHTML === 'start') { - if (running) button.disabled = true; - else button.disabled = false; + button.disabled = running; } if (button.innerHTML === 'pause') { - if (running) button.disabled = false; - else button.disabled = true; + button.disabled = !running; } }); } @@ -67,18 +58,15 @@ class Painter { if (running) status.classList.remove('status_stopped'); else status.classList.add('status_stopped'); } - toggleCell(target) { - target.classList.toggle('live'); - } - paintTbody(matrix, tableWidth) { + getNewTbody(matrix, tableWidth) { // заполнение тела таблицы const columns = matrix[0].length; const size = tableWidth / columns; - const tbody = document.createElement('tbody'); + let tbody = document.createElement('tbody'); matrix.forEach((row) => { - const tr = document.createElement('tr'); + let tr = document.createElement('tr'); row.forEach((cell) => { - const td = document.createElement('td'); + let td = document.createElement('td'); this.setTdClass(td, cell); td.style.width = `${size}px`; td.style.height = `${size}px`; @@ -88,14 +76,14 @@ class Painter { }); return tbody; } - newTable(matrix) { + initTable(matrix) { // для создания и ресайза таблицы const { table } = this; - const tbody = this.paintTbody(matrix, table.clientWidth); + const tbody = this.getNewTbody(matrix, table.clientWidth); if (table.children.length) table.replaceChild(tbody, table.children[0]); else table.appendChild(tbody); } - repaintTable(matrix) { + changeTable(matrix) { // изменение класса у ячеек таблицы const { table } = this; const tbody = table.children[0];