-
Notifications
You must be signed in to change notification settings - Fork 20
/
Copy pathexample-puzzle.html
136 lines (124 loc) · 4.17 KB
/
example-puzzle.html
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
<!DOCTYPE html>
<html>
<head>
<title>Example - Puzzle</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<!-- Replace with a separate style.css file if you want -->
<style type="text/css">
body {
background: black;
color: white;
}
</style>
<!-- These 2 script tags are required for the 3D effect.
You can remove these if you disabled THREE_SETTINGS. -->
<script async src="https://unpkg.com/[email protected]/dist/es-module-shims.js"></script>
<script type="importmap">
{
"imports": {
"three": "./three-0.156.1/three.module.min.js",
"three/addons/": "./three-0.156.1/addons/"
}
}
</script>
<script type="module">
import * as qx from "./qx82/qx.js";
import * as qxa from "./qx82/qxa.js";
// Full image.
let fullImg;
// Size of a puzzle piece.
const PIECE_WIDTH = 64, PIECE_HEIGHT = 42;
// How many rows and columns of puzzle pieces.
const GRID_ROWS = 4, GRID_COLS = 4;
// The current puzzle layout. -1 is the empty cell.
let pieces = [
[ 4, 0, 1, 3, ],
[ 8, 6, 2, 7, ],
[ 5, 13, 9, 11, ],
[ -1, 12, 10, 14, ]
];
// Current row/column.
let curRow = 0, curCol = 0;
async function main() {
qx.color(7, 0);
qx.cls();
qx.locate(1, 1);
qx.print("Loading...");
fullImg = await qxa.loadImage("assets/colmar.png");
while (true) {
drawBoard();
await processInput();
}
}
function drawBoard() {
qx.cls();
// Draw each piece.
for (let i = 0; i < GRID_ROWS; i++) {
for (let j = 0; j < GRID_COLS; j++) {
const pieceNumber = pieces[i][j];
drawPiece(pieceNumber, j * PIECE_WIDTH, i * PIECE_HEIGHT,
i === curRow && j === curCol);
}
}
qx.locate(1, 22);
if (hasWon()) {
qx.color(12, 0);
qx.print("* YOU WON! CONGRATULATIONS! *");
} else {
qx.color(7, 0);
qx.print("Arrows: select, Enter: slide");
}
}
function drawPiece(pieceNumber, x, y, isCurrent) {
// The piece number tells us what part of the image to draw.
const row = Math.floor(pieceNumber / GRID_COLS);
const col = pieceNumber % GRID_COLS;
// Draw the rectangular part of the image that corresponds to
// the piece.
qx.drawImageRect(x, y, fullImg,
col * PIECE_WIDTH, row * PIECE_HEIGHT, PIECE_WIDTH, PIECE_HEIGHT);
qx.color(isCurrent ? 14 : 0);
qx.drawRect(x, y, PIECE_WIDTH, PIECE_HEIGHT);
}
async function processInput() {
const k = await qxa.key();
// If the player pressed an arrow key, move cursor.
if (k === "ArrowDown") curRow = (curRow + 1) % GRID_ROWS;
if (k === "ArrowUp") curRow = (curRow - 1 + GRID_ROWS) % GRID_ROWS;
if (k === "ArrowLeft") curCol = (curCol - 1 + GRID_COLS) % GRID_COLS;
if (k === "ArrowRight") curCol = (curCol + 1) % GRID_COLS;
// If player pressed Enter or the A button, swap the piece
// if it's adjacent to an empty space.
if (k === "Enter" || k === "ButtonA") swapCurPiece();
}
function swapCurPiece() {
const neighborDirs =
[ {c:-1,r:0}, {c:1,r:0}, {c:0,r:-1}, {c:0,r:1} ];
for (const dir of neighborDirs) {
const newRow = curRow + dir.r;
const newCol = curCol + dir.c;
console.warn(`${curRow} + ${dir.r} = ${newRow}, ${curCol} + ${dir.c} = ${newCol}`);
if (newRow >= 0 && newRow < GRID_ROWS && newCol >= 0 &&
newCol < GRID_COLS && pieces[newRow][newCol] === -1) {
// Swap it with the empty space.
pieces[newRow][newCol] = pieces[curRow][curCol];
pieces[curRow][curCol] = -1;
return;
}
}
}
function hasWon() {
// We have won if all pieces are in 0, 1, 2, 3, ... order,
// except the last cell which should be -1.
const flat = pieces.flat();
for (let i = 0; i < GRID_ROWS * GRID_COLS - 1; i++) {
if (flat[i] !== i) return false;
}
return true;
}
window.addEventListener("load", () => qx.init(main));
</script>
</head>
<body>
</body>
</html>