Compare commits
2 Commits
demo/2048
...
starter/jq
| Author | SHA1 | Date | |
|---|---|---|---|
| 0328563e1d | |||
| d9781431c6 |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -0,0 +1 @@
|
|||||||
|
node_modules
|
||||||
|
|||||||
39
base.css
Normal file
39
base.css
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
body {
|
||||||
|
padding: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.box {
|
||||||
|
border: 4px solid #28e;
|
||||||
|
border-radius: 4px;
|
||||||
|
padding: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vertical-stack {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
flex-wrap: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vertical-stack > * {
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vertical-stack > *:last-child {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-button {
|
||||||
|
border: 0.5px solid #558;
|
||||||
|
background: hsl(228, 100%, 90%);
|
||||||
|
background: linear-gradient(150deg, hsl(228, 100%, 95%) 0%, hsl(228, 100%, 80%) 100%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer {
|
||||||
|
text-align: center;
|
||||||
|
font-size: small;
|
||||||
|
font-family: "Dosis", sans-serif;
|
||||||
|
font-optical-sizing: auto;
|
||||||
|
font-weight: 600;
|
||||||
|
font-style: normal;
|
||||||
|
}
|
||||||
|
|
||||||
44
index.html
44
index.html
@@ -1,43 +1,29 @@
|
|||||||
<!doctype html>
|
<!doctype html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<title>Grid Demo</title>
|
<title>My Demo</title>
|
||||||
<meta name="viewport" content="width=device-width">
|
<meta name="viewport" content="width=device-width">
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<style type="text/css">
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||||
table {
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||||
border: 1px solid black;
|
<link href="https://fonts.googleapis.com/css2?family=Dosis:wght@200..800&display=swap" rel="stylesheet">
|
||||||
}
|
<link type="text/css" rel="stylesheet" href="reset.css" />
|
||||||
tr {
|
<link type="text/css" rel="stylesheet" href="base.css" />
|
||||||
border: 1px solid black;
|
<link type="text/css" rel="stylesheet" href="styles.css" />
|
||||||
}
|
<script src="node_modules/jquery/dist/jquery.js"></script>
|
||||||
td {
|
|
||||||
border: 1px solid black;
|
|
||||||
}
|
|
||||||
td.hidden {
|
|
||||||
background-color: #f88;
|
|
||||||
}
|
|
||||||
td.visible {
|
|
||||||
background-color: #8f8;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<fieldset>
|
<div class="vertical-stack">
|
||||||
<legend title="asdf">Grid</legend>
|
<fieldset id="demo" class="box">
|
||||||
|
<legend title="asdf">My Demo</legend>
|
||||||
<div id="output"></div>
|
<div id="output"></div>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
|
|
||||||
<section>
|
<section class="box">
|
||||||
<button id="btnLeft">Left</button>
|
<button id="btnGo" class="action-button">Go</button>
|
||||||
<button id="btnUp">Up</button>
|
|
||||||
<button id="btnDown">Down</button>
|
|
||||||
<button id="btnRight">Right</button>
|
|
||||||
</section>
|
|
||||||
<section>
|
|
||||||
<button id="btnStart">Start</button>
|
|
||||||
<button id="btnRedraw">Redraw</button>
|
|
||||||
</section>
|
</section>
|
||||||
|
</div>
|
||||||
|
<footer class="footer">copyright © 2024 <a href="https://sfcoder.me">Tom Hicks</a> licensed under <a href="https://opensource.org/license/gpl-2-0">GPL v2.0 or later</a></footer>
|
||||||
<script src="main.js"></script>
|
<script src="main.js"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
475
main.js
475
main.js
@@ -1,470 +1,5 @@
|
|||||||
let gameBoard;
|
(()=>{
|
||||||
|
jQuery(() => {
|
||||||
const Direction = Object.freeze({
|
console.log('Ready');
|
||||||
Up: { name: "up" },
|
})
|
||||||
Right: { name: "right" },
|
})();
|
||||||
Down: { name: "down" },
|
|
||||||
Left: { name: "left" },
|
|
||||||
get values() { return Object.values(Direction); },
|
|
||||||
fromName(name) { return this.values.filter(i=>i.name == name)[0] || null; },
|
|
||||||
});
|
|
||||||
|
|
||||||
const setup = () => {
|
|
||||||
document.addEventListener('keydown', ev => {
|
|
||||||
switch (ev.key) {
|
|
||||||
case 'w':
|
|
||||||
case 'ArrowUp':
|
|
||||||
playMove(gameBoard, Direction.Up);
|
|
||||||
break;
|
|
||||||
case 'a':
|
|
||||||
case 'ArrowLeft':
|
|
||||||
playMove(gameBoard, Direction.Left);
|
|
||||||
break;
|
|
||||||
case 's':
|
|
||||||
case 'ArrowDown':
|
|
||||||
playMove(gameBoard, Direction.Down);
|
|
||||||
break;
|
|
||||||
case 'd':
|
|
||||||
case 'ArrowRight':
|
|
||||||
playMove(gameBoard, Direction.Right);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
console.log(ev.key);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
binding = new Binding(['btnStart', 'btnUp', 'btnRight', 'btnDown', 'btnLeft', 'btnRedraw']);
|
|
||||||
const start = () => {
|
|
||||||
gameBoard = new GameBoard(new Grid(4, 4));
|
|
||||||
// gameBoard = new GameBoard(
|
|
||||||
// makeDevGrid([ // can't slide down or left
|
|
||||||
// [2,1,3,4],
|
|
||||||
// [3,5,7,9],
|
|
||||||
// [1,2,1,2],
|
|
||||||
// [3,4,5,0],
|
|
||||||
// ])
|
|
||||||
// );
|
|
||||||
gameBoard.addRandomTile();
|
|
||||||
redraw(gameBoard);
|
|
||||||
};
|
|
||||||
binding.btnStart.on('click', start);
|
|
||||||
|
|
||||||
binding.btnUp.on('click', () => {
|
|
||||||
playMove(gameBoard, Direction.Up);
|
|
||||||
});
|
|
||||||
|
|
||||||
binding.btnRight.on('click', () => {
|
|
||||||
playMove(gameBoard, Direction.Right);
|
|
||||||
});
|
|
||||||
|
|
||||||
binding.btnDown.on('click', () => {
|
|
||||||
playMove(gameBoard, Direction.Down);
|
|
||||||
});
|
|
||||||
|
|
||||||
binding.btnLeft.on('click', () => {
|
|
||||||
playMove(gameBoard, Direction.Left);
|
|
||||||
});
|
|
||||||
|
|
||||||
binding.btnRedraw.on('click', () => {
|
|
||||||
redraw(gameBoard);
|
|
||||||
});
|
|
||||||
|
|
||||||
start();
|
|
||||||
}
|
|
||||||
|
|
||||||
class ElementBinding {
|
|
||||||
#element;
|
|
||||||
|
|
||||||
constructor (element) {
|
|
||||||
this.#element = element;
|
|
||||||
}
|
|
||||||
on(event, action) {
|
|
||||||
// TODO(tom): Test multiple bindings.
|
|
||||||
if (this.#element != null) {
|
|
||||||
this.#element.addEventListener(event, action);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
class Binding {
|
|
||||||
constructor(elementIds) {
|
|
||||||
elementIds.forEach(i =>
|
|
||||||
this[i] = new ElementBinding(document.getElementById(i))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO(tom): This should take GameBoard instead of Grid or be moved into GameBoard.
|
|
||||||
const playMove = (gameBoard, direction) => {
|
|
||||||
if (gameBoard.slideInDirection(direction)) {
|
|
||||||
gameBoard.addRandomTile();
|
|
||||||
redraw(gameBoard)
|
|
||||||
} else {
|
|
||||||
// alert(`Unable to slide ${direction.name}.`);
|
|
||||||
}
|
|
||||||
checkForEnd(gameBoard);
|
|
||||||
}
|
|
||||||
|
|
||||||
const redraw = (gameBoard) => {
|
|
||||||
const element = document.getElementById('output');
|
|
||||||
|
|
||||||
let text = "<table cellspacing=\"0\">\n";
|
|
||||||
for (let rowIndex = 0; rowIndex < gameBoard.numRows; rowIndex++) {
|
|
||||||
text += " <tr>\n";
|
|
||||||
for (let columnIndex = 0; columnIndex < gameBoard.numColumns; columnIndex++) {
|
|
||||||
const tile = gameBoard.getTile(rowIndex, columnIndex);
|
|
||||||
text += " <td class=\"" + (tile.isVisible ? "visible " : "hidden ") + "\">" + tile.value + "</td>\n";
|
|
||||||
}
|
|
||||||
text += " </tr>\n";
|
|
||||||
}
|
|
||||||
text += "</table>";
|
|
||||||
element.innerHTML = text;
|
|
||||||
}
|
|
||||||
|
|
||||||
const checkForEnd = (gameBoard) => {
|
|
||||||
if (gameBoard.gameIsLost()) {
|
|
||||||
alert('A loser is you!');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const makeDevGrid = (matrix) => {
|
|
||||||
const numRows = matrix.length;
|
|
||||||
const numColumns = matrix[0].length;
|
|
||||||
const grid = new Grid(numRows, numColumns);
|
|
||||||
for (let rowIndex = 0; rowIndex < numRows; rowIndex++) {
|
|
||||||
const row = matrix[rowIndex];
|
|
||||||
for (let colIndex = 0; colIndex < numColumns; colIndex++) {
|
|
||||||
const value = row[colIndex];
|
|
||||||
const tile = grid.getTile(rowIndex, colIndex);
|
|
||||||
tile.value = value;
|
|
||||||
tile.isVisible = value > 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return grid;
|
|
||||||
}
|
|
||||||
/*
|
|
||||||
[0,0,0,0],
|
|
||||||
[0,0,1,0],
|
|
||||||
[0,0,0,0],
|
|
||||||
[2,1,2,2],
|
|
||||||
can't slide down.
|
|
||||||
[0,0,1,0],
|
|
||||||
[0,0,0,0],
|
|
||||||
[0,0,0,0],
|
|
||||||
[3,2,3,0],
|
|
||||||
can't slide down.
|
|
||||||
can't slide left.
|
|
||||||
up()
|
|
||||||
[1,0,1,0],
|
|
||||||
[0,0,3,0],
|
|
||||||
[0,0,0,0],
|
|
||||||
[3,2,0,0], either the 3 doesn't go up or the 1 bounces it back?
|
|
||||||
// fixed
|
|
||||||
*/
|
|
||||||
|
|
||||||
class GameBoard {
|
|
||||||
#grid;
|
|
||||||
|
|
||||||
get numColumns() {
|
|
||||||
return this.#grid.numColumns;
|
|
||||||
}
|
|
||||||
|
|
||||||
get numRows() {
|
|
||||||
return this.#grid.numRows;
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor(grid) {
|
|
||||||
this.#grid = grid;
|
|
||||||
}
|
|
||||||
|
|
||||||
getTile(row, column) {
|
|
||||||
return this.#grid.getTile(row, column);
|
|
||||||
}
|
|
||||||
|
|
||||||
addRandomTile() {
|
|
||||||
const hiddenTiles = this.#grid.tiles.filter(tile => !tile.isVisible);
|
|
||||||
if (hiddenTiles.length <= 0) { return; }
|
|
||||||
const randomIndex = Math.floor(Math.random() * hiddenTiles.length);
|
|
||||||
const tile = hiddenTiles[randomIndex];
|
|
||||||
// TODO(tom): Make this random of either 1 or 2 and heavily weight 1.
|
|
||||||
tile.value = 1;
|
|
||||||
tile.isVisible = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
slideInDirection (direction) {
|
|
||||||
return GameBoard.#slideInDirection_impl(direction, this.#grid);
|
|
||||||
}
|
|
||||||
|
|
||||||
gameIsLost() {
|
|
||||||
return !this.#canSlideInDirection(Direction.Up)
|
|
||||||
&& !this.#canSlideInDirection(Direction.Right)
|
|
||||||
&& !this.#canSlideInDirection(Direction.Down)
|
|
||||||
&& !this.#canSlideInDirection(Direction.Left);
|
|
||||||
}
|
|
||||||
|
|
||||||
#canSlideInDirection(direction) {
|
|
||||||
const clonedGrid = this.#grid.clone();
|
|
||||||
return GameBoard.#slideInDirection_impl(direction, clonedGrid);
|
|
||||||
}
|
|
||||||
|
|
||||||
static #slideInDirection_impl (direction, grid) {
|
|
||||||
const settings = GameBoard.#getSettingsForDirection(direction);
|
|
||||||
const length = settings.getLength(grid);
|
|
||||||
let didChange = false;
|
|
||||||
|
|
||||||
for (let index = 0; index < length; index++) {
|
|
||||||
const vector = settings.getVector(grid, index);
|
|
||||||
const swap = settings.swap.bind(this, grid, vector, index);
|
|
||||||
const initialFromIndex = settings.getInitialFromIndex(vector.length);
|
|
||||||
const initialToIndex = settings.getInitialToIndex(vector.length);
|
|
||||||
const didChangeThisPass = GameBoard.#slide(
|
|
||||||
initialFromIndex,
|
|
||||||
initialToIndex,
|
|
||||||
vector,
|
|
||||||
settings.advance,
|
|
||||||
swap,
|
|
||||||
settings.merge,
|
|
||||||
settings.testForDone
|
|
||||||
);
|
|
||||||
didChange = didChange || didChangeThisPass;
|
|
||||||
}
|
|
||||||
return didChange;
|
|
||||||
}
|
|
||||||
|
|
||||||
static #getSettingsForDirection(direction) {
|
|
||||||
const settings = new Settings();
|
|
||||||
settings.merge = (from, to) => {
|
|
||||||
to.value++;
|
|
||||||
from.value = 0;
|
|
||||||
from.isVisible = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
if (direction === Direction.Up || direction === Direction.Left) {
|
|
||||||
settings.advance = i => i + 1;
|
|
||||||
settings.testForDone = (fromIndex, length) => fromIndex >= length;
|
|
||||||
settings.getInitialFromIndex = () => 1;
|
|
||||||
settings.getInitialToIndex = () => 0;
|
|
||||||
} else if (direction === Direction.Down || direction === Direction.Right) {
|
|
||||||
settings.advance = i => i - 1;
|
|
||||||
settings.testForDone = fromIndex => fromIndex < 0;
|
|
||||||
settings.getInitialFromIndex = (length) => length - 2;
|
|
||||||
settings.getInitialToIndex = (length) => length - 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (direction === Direction.Up || direction === Direction.Down) {
|
|
||||||
settings.getVector = (grid, index) => grid.getColumn(index);
|
|
||||||
settings.swap = (grid, vector, columnIndex, from, to) => {
|
|
||||||
grid.swapTiles(from, columnIndex, to, columnIndex);
|
|
||||||
const tempTile = vector[to];
|
|
||||||
vector[to] = vector[from];
|
|
||||||
vector[from] = tempTile;
|
|
||||||
}
|
|
||||||
settings.getLength = grid => grid.numColumns;
|
|
||||||
} else if (direction === Direction.Left || direction === Direction.Right) {
|
|
||||||
settings.getVector = (grid, index) => grid.getRow(index);
|
|
||||||
settings.swap = (grid, vector, rowIndex, from, to) => {
|
|
||||||
grid.swapTiles(rowIndex, from, rowIndex, to);
|
|
||||||
const tempTile = vector[to];
|
|
||||||
vector[to] = vector[from];
|
|
||||||
vector[from] = tempTile;
|
|
||||||
};
|
|
||||||
settings.getLength = grid => grid.numRows;
|
|
||||||
}
|
|
||||||
return settings;
|
|
||||||
}
|
|
||||||
|
|
||||||
static #slide(fromIndex, toIndex, vector, advance, swap, merge, testForDone) {
|
|
||||||
let didChange = false;
|
|
||||||
let hasMergedPrevious = false;
|
|
||||||
while(!testForDone(fromIndex, vector.length)) {
|
|
||||||
const fromTile = vector[fromIndex];
|
|
||||||
const toTile = toIndex < vector.length && toIndex >= 0 ? vector[toIndex] : null;
|
|
||||||
if (toTile == null) {
|
|
||||||
// Advance toIndex
|
|
||||||
toIndex = advance(toIndex);
|
|
||||||
hasMergedPrevious = false;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (fromIndex == toIndex) {
|
|
||||||
// Advance fromIndex
|
|
||||||
fromIndex = advance(fromIndex);
|
|
||||||
hasMergedPrevious = false;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (!toTile.isVisible) {
|
|
||||||
if (fromTile.isVisible) {
|
|
||||||
swap(fromIndex, toIndex);
|
|
||||||
// Advance fromIndex
|
|
||||||
fromIndex = advance(fromIndex);
|
|
||||||
hasMergedPrevious = false;
|
|
||||||
didChange = true;
|
|
||||||
} else {
|
|
||||||
// Advance fromIndex
|
|
||||||
fromIndex = advance(fromIndex);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (fromTile.isVisible) {
|
|
||||||
if (fromTile.value == toTile.value) {
|
|
||||||
if (hasMergedPrevious) {
|
|
||||||
// Advance toIndex
|
|
||||||
toIndex = advance(toIndex);
|
|
||||||
hasMergedPrevious = false;
|
|
||||||
didChange = true;
|
|
||||||
} else {
|
|
||||||
// Merge tiles
|
|
||||||
merge(fromTile, toTile);
|
|
||||||
// Advance fromIndex
|
|
||||||
fromIndex = advance(fromIndex);
|
|
||||||
hasMergedPrevious = true;
|
|
||||||
didChange = true;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Advance toIndex
|
|
||||||
toIndex = advance(toIndex);
|
|
||||||
hasMergedPrevious = false;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
fromIndex = advance(fromIndex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return didChange;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
class Grid {
|
|
||||||
#tiles = [];
|
|
||||||
#rows = [];
|
|
||||||
#numRows;
|
|
||||||
#numColumns;
|
|
||||||
get tiles() { return [...this.#tiles]; }
|
|
||||||
get numRows() { return this.#numRows; }
|
|
||||||
get numColumns() { return this.#numColumns; }
|
|
||||||
|
|
||||||
constructor(numRows, numColumns) {
|
|
||||||
this.#numRows = numRows;
|
|
||||||
this.#numColumns = numColumns;
|
|
||||||
for (let rowIndex = 0; rowIndex < numRows; rowIndex++) {
|
|
||||||
const row = [];
|
|
||||||
for (let columnIndex = 0; columnIndex < this.numColumns; columnIndex++) {
|
|
||||||
const tile = new Tile();
|
|
||||||
this.#tiles.push(tile);
|
|
||||||
row.push(tile);
|
|
||||||
}
|
|
||||||
this.#rows.push(row);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
clone() {
|
|
||||||
const newGrid = new Grid(this.numRows, this.numColumns);
|
|
||||||
for (let rowIndex = 0; rowIndex < this.numRows; rowIndex++) {
|
|
||||||
for (let columnIndex = 0; columnIndex < this.numColumns; columnIndex++) {
|
|
||||||
const newTile = newGrid.getTile(rowIndex, columnIndex);
|
|
||||||
const oldTile = this.getTile(rowIndex, columnIndex);
|
|
||||||
newTile.value = oldTile.value;
|
|
||||||
newTile.isVisible = oldTile.isVisible;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return newGrid;
|
|
||||||
}
|
|
||||||
|
|
||||||
getColumn (columnIndex) {
|
|
||||||
let vector = [];
|
|
||||||
for (let rowIndex = 0; rowIndex < this.numRows; rowIndex++) {
|
|
||||||
vector.push(this.getTile(rowIndex, columnIndex));
|
|
||||||
}
|
|
||||||
return vector;
|
|
||||||
}
|
|
||||||
|
|
||||||
getRow (rowIndex) {
|
|
||||||
let vector = [];
|
|
||||||
for (let columnIndex = 0; columnIndex < this.numColumns; columnIndex++) {
|
|
||||||
vector.push(this.getTile(rowIndex, columnIndex));
|
|
||||||
}
|
|
||||||
return vector;
|
|
||||||
}
|
|
||||||
|
|
||||||
getTile (row, column) {
|
|
||||||
if (row >= 0 && row < this.numRows && column >= 0 && column < this.numColumns) {
|
|
||||||
// TODO(tom): Check that this is the right order.
|
|
||||||
return this.#rows[column][row]
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
setTile (row, column, tile) {
|
|
||||||
if (row >= 0 && row < this.numRows && column >= 0 && column < this.numColumns) {
|
|
||||||
// TODO(tom): Check that this is the right order.
|
|
||||||
this.#rows[column][row] = tile;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
swapTiles (fromRow, fromColumn, toRow, toColumn) {
|
|
||||||
if (
|
|
||||||
fromRow >= 0 && fromRow < this.numRows
|
|
||||||
&& fromColumn >= 0 && fromColumn < this.numColumns
|
|
||||||
&& toRow >= 0 && toRow < this.numRows
|
|
||||||
&& toColumn >= 0 && toColumn < this.numColumns) {
|
|
||||||
const fromTile = this.getTile(fromRow, fromColumn);
|
|
||||||
const toTile = this.getTile(toRow, toColumn);
|
|
||||||
this.setTile(fromRow, fromColumn, toTile);
|
|
||||||
this.setTile(toRow, toColumn, fromTile);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
toString (format = t => t.value) {
|
|
||||||
return `[[${format(this.getTile(0, 0))},${format(this.getTile(0, 1))},${format(this.getTile(0, 2))},${format(this.getTile(0, 3))}],
|
|
||||||
[${format(this.getTile(1, 0))},${format(this.getTile(1, 1))},${format(this.getTile(1, 2))},${format(this.getTile(1, 3))}],
|
|
||||||
[${format(this.getTile(2, 0))},${format(this.getTile(2, 1))},${format(this.getTile(2, 2))},${format(this.getTile(2, 3))}],
|
|
||||||
[${format(this.getTile(3, 0))},${format(this.getTile(3, 1))},${format(this.getTile(3, 2))},${format(this.getTile(3, 3))}]]`;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
class GridPosition {
|
|
||||||
#row;
|
|
||||||
#column;
|
|
||||||
constructor (row, column) {
|
|
||||||
this.#row = row || 0;
|
|
||||||
this.#column = column || 0;
|
|
||||||
}
|
|
||||||
get row() { return this.#row; }
|
|
||||||
get column() { return this.#column; }
|
|
||||||
equals (other) {
|
|
||||||
return other !== null && other !== undefined
|
|
||||||
&& this.#row === other.#row
|
|
||||||
&& this.#column === other.#column;
|
|
||||||
}
|
|
||||||
toString() {
|
|
||||||
return `{ row: ${this.#row}, column: ${this.#column} }`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class Tile {
|
|
||||||
value;
|
|
||||||
isVisible;
|
|
||||||
constructor () {
|
|
||||||
this.value = 0;
|
|
||||||
this.isVisible = false;
|
|
||||||
}
|
|
||||||
equals (other) {
|
|
||||||
return other !== null && other !== undefined
|
|
||||||
&& this.value === other.value
|
|
||||||
&& this.isVisible === other.isVisible;
|
|
||||||
}
|
|
||||||
toString() {
|
|
||||||
return `{ value: ${this.value}, isVisible: ${this.isVisible ? "true" : "false"} }`;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
class Settings {
|
|
||||||
merge;
|
|
||||||
advance;
|
|
||||||
testForDone;
|
|
||||||
getInitialFromIndex;
|
|
||||||
getInitialToIndex;
|
|
||||||
getVector;
|
|
||||||
getLength;
|
|
||||||
swap;
|
|
||||||
};
|
|
||||||
|
|
||||||
setup();
|
|
||||||
|
|||||||
11
package.json
Normal file
11
package.json
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"name": "demo",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"main": "index.js",
|
||||||
|
"author": "Tom Hicks <headhunter3@gmail.com>",
|
||||||
|
"license": "GLPv2",
|
||||||
|
"private": true,
|
||||||
|
"dependencies": {
|
||||||
|
"jquery": "^3.7.1"
|
||||||
|
}
|
||||||
|
}
|
||||||
8
reset.css
Normal file
8
reset.css
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
fieldset {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
0
styles.css
Normal file
0
styles.css
Normal file
8
yarn.lock
Normal file
8
yarn.lock
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
|
||||||
|
# yarn lockfile v1
|
||||||
|
|
||||||
|
|
||||||
|
jquery@^3.7.1:
|
||||||
|
version "3.7.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/jquery/-/jquery-3.7.1.tgz#083ef98927c9a6a74d05a6af02806566d16274de"
|
||||||
|
integrity sha512-m4avr8yL8kmFN8psrbFFFmB/If14iN5o9nw/NgnnM+kybDJpRsAynV2BsfpTYrTRysYUdADVD7CkUUizgkpLfg==
|
||||||
Reference in New Issue
Block a user