-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathGrid.js
144 lines (126 loc) · 4.34 KB
/
Grid.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
const GRID_SIZE = 4;
const CELL_SIZE = 20;
const CELL_GAP = 2;
/**
* objects of class Grid will have
* - a property `cells` which is an array of Cell objects
* - upon construction, a series of div elements with class `cell` will be added as child elements to the gridElement that is passed in the constructor
*/
export default class Grid {
#cells; // private property that can only be accessed inside Grid
constructor(gridElement) {
gridElement.style.setProperty("--grid-size", GRID_SIZE);
gridElement.style.setProperty("--cell-size", `${CELL_SIZE}vmin`);
gridElement.style.setProperty("--cell-gap", `${CELL_GAP}vmin`);
// for each cell (html) element, return a Cell object
this.#cells = createCellElements(gridElement).map((cellElement, index) => {
return new Cell(
cellElement,
index % GRID_SIZE, // x
Math.floor(index / GRID_SIZE) // y
);
});
}
get cells() {
return this.#cells;
}
get cellsByColumn() {
return this.#cells.reduce((cellGrid, cell) => {
cellGrid[cell.x] = cellGrid[cell.x] || []; // if cellGrid[cell.x] doesn't exist yet, initialize it as an empty array
cellGrid[cell.x][cell.y] = cell;
return cellGrid;
}, []);
}
get cellsByRow() {
return this.#cells.reduce((cellGrid, cell) => {
cellGrid[cell.y] = cellGrid[cell.y] || []; // if cellGrid[cell.x] doesn't exist yet, initialize it as an empty array
cellGrid[cell.y][cell.x] = cell;
return cellGrid;
}, []);
}
// bind the private property `emptyCells` to a function, which will be called when that property is looked up
get #emptyCells() {
return this.#cells.filter((cell) => !cell.tile);
}
// return a random empty Cell object (where tile is null)
randomEmptyCell() {
const randomIndex = Math.floor(Math.random() * this.#emptyCells.length);
return this.#emptyCells[randomIndex];
}
}
/**
* a cell Object will have
* - a `cellElement` property, which is a div element with class name of `cell`
* - x coordinate
* - y coordinate
* - tile
*/
class Cell {
#cellElement;
#x;
#y;
#tile;
#mergeTile;
constructor(cellElement, x, y) {
this.#cellElement = cellElement;
this.#x = x;
this.#y = y;
}
get x() {
return this.#x;
}
get y() {
return this.#y;
}
get tile() {
return this.#tile;
}
/**
* set the Tile object of a Cell object. First the Cell object's Tile object is assigned the passed-in tileObj.
* Then the Cell object's new Tile object will be assigned the x, y coordinates of the Cell object, so it can appear (with css) that the Tile object is on the cell.
**/
set tile(tileObj) {
this.#tile = tileObj;
if (!tileObj) return;
this.#tile.x = this.#x; // this.#tile === tileObj, pointing to the same space in memory
this.#tile.y = this.#y;
}
get mergeTile() {
return this.#mergeTile;
}
set mergeTile(value) {
this.#mergeTile = value;
if (!value) return;
this.#mergeTile.x = this.#x;
this.#mergeTile.y = this.#y;
}
/**
* Can the current Cell object accept a Tile object? Yes if:
* - the tile of the current cell is null
* - if the value of the Tile object on current Cell object is equal to the value of the Tile object being passed in
* - if don't have a mergeTile specified. If you already have a mergeTile, we can't do another merge => we can only merge one Tile at a time
**/
canAccept(tile) {
return !this.tile || (!this.mergeTile && this.tile.value === tile.value);
}
mergeTiles() {
if (!this.tile || !this.mergeTile) return;
this.tile.value = this.tile.value + this.mergeTile.value; // the Tile object on the Grid object, its value will be updated to be the sum of the current tile value and the merge Tile
this.mergeTile.remove(); // remove the mergeTile (which is a Tile object) from the DOM
this.mergeTile = null; // set the mergeTile property of the Cell object to null
}
}
/**
* @param {HTML Element} gridElement
* @returns an array of HTML elements (which are `div`s with class name of `cell`) which will also be appended as child elements to the gridElement
*/
function createCellElements(gridElement) {
const cells = [];
for (let i = 0; i < GRID_SIZE * GRID_SIZE; i++) {
const cell = document.createElement("div");
cell.classList.add("cell");
cells.push(cell);
gridElement.append(cell);
}
return cells;
}