create-template-html-css 1.9.0 → 2.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +80 -0
- package/COMPONENTS-GALLERY.html +735 -747
- package/README.md +179 -2
- package/bin/cli.js +15 -3
- package/package.json +1 -1
- package/templates/blackjack/index.html +97 -0
- package/templates/blackjack/script.js +381 -0
- package/templates/blackjack/style.css +452 -0
- package/templates/breakout/index.html +56 -0
- package/templates/breakout/script.js +387 -0
- package/templates/breakout/style.css +239 -0
- package/templates/connect-four/index.html +78 -0
- package/templates/connect-four/script.js +413 -0
- package/templates/connect-four/style.css +426 -0
- package/templates/dice-game/index.html +99 -0
- package/templates/dice-game/script.js +291 -0
- package/templates/dice-game/style.css +403 -0
- package/templates/flappy-bird/index.html +47 -0
- package/templates/flappy-bird/script.js +394 -0
- package/templates/flappy-bird/style.css +243 -0
- package/templates/game-2048/index.html +59 -0
- package/templates/game-2048/script.js +269 -0
- package/templates/game-2048/style.css +281 -0
- package/templates/pong/index.html +90 -0
- package/templates/pong/script.js +364 -0
- package/templates/pong/style.css +371 -0
- package/templates/rock-paper-scissors/index.html +84 -0
- package/templates/rock-paper-scissors/script.js +199 -0
- package/templates/rock-paper-scissors/style.css +295 -0
- package/templates/simon-says/index.html +64 -0
- package/templates/simon-says/script.js +206 -0
- package/templates/simon-says/style.css +250 -0
- package/templates/slot-machine/index.html +112 -0
- package/templates/slot-machine/script.js +238 -0
- package/templates/slot-machine/style.css +464 -0
- package/templates/tetris/index.html +84 -0
- package/templates/tetris/script.js +447 -0
- package/templates/tetris/style.css +286 -0
- package/templates/whack-a-mole/index.html +85 -0
- package/templates/whack-a-mole/script.js +172 -0
- package/{demo-games/snake-game → templates/whack-a-mole}/style.css +114 -97
- package/PUBLISH-GUIDE.md +0 -112
- package/create-template-html-css-1.8.0.tgz +0 -0
- package/demo-games/guess-number/index.html +0 -71
- package/demo-games/guess-number/script.js +0 -216
- package/demo-games/guess-number/style.css +0 -337
- package/demo-games/memory-game/index.html +0 -50
- package/demo-games/memory-game/script.js +0 -216
- package/demo-games/memory-game/style.css +0 -288
- package/demo-games/snake-game/index.html +0 -61
- package/demo-games/snake-game/script.js +0 -360
- package/demo-games/tic-tac-toe/index.html +0 -57
- package/demo-games/tic-tac-toe/script.js +0 -156
- package/demo-games/tic-tac-toe/style.css +0 -244
|
@@ -0,0 +1,413 @@
|
|
|
1
|
+
// Game constants
|
|
2
|
+
const ROWS = 6;
|
|
3
|
+
const COLS = 7;
|
|
4
|
+
const PLAYER1 = 1;
|
|
5
|
+
const PLAYER2 = 2;
|
|
6
|
+
|
|
7
|
+
// Game state
|
|
8
|
+
let gameState = {
|
|
9
|
+
board: [],
|
|
10
|
+
currentPlayer: PLAYER1,
|
|
11
|
+
gameOver: false,
|
|
12
|
+
gameMode: 'pvp', // 'pvp' or 'pvc'
|
|
13
|
+
difficulty: 'medium',
|
|
14
|
+
score1: parseInt(localStorage.getItem('connect4Score1')) || 0,
|
|
15
|
+
score2: parseInt(localStorage.getItem('connect4Score2')) || 0,
|
|
16
|
+
isAnimating: false
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
// Initialize board
|
|
20
|
+
function initBoard() {
|
|
21
|
+
gameState.board = Array(ROWS).fill().map(() => Array(COLS).fill(0));
|
|
22
|
+
gameState.currentPlayer = PLAYER1;
|
|
23
|
+
gameState.gameOver = false;
|
|
24
|
+
renderBoard();
|
|
25
|
+
updateTurnIndicator();
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// Render board
|
|
29
|
+
function renderBoard() {
|
|
30
|
+
const boardElement = document.getElementById('gameBoard');
|
|
31
|
+
boardElement.innerHTML = '';
|
|
32
|
+
|
|
33
|
+
for (let col = 0; col < COLS; col++) {
|
|
34
|
+
const column = document.createElement('div');
|
|
35
|
+
column.className = 'column';
|
|
36
|
+
column.dataset.col = col;
|
|
37
|
+
|
|
38
|
+
// Add hover effect
|
|
39
|
+
column.addEventListener('mouseenter', () => {
|
|
40
|
+
if (!gameState.gameOver && !gameState.isAnimating) {
|
|
41
|
+
column.classList.add('hover');
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
column.addEventListener('mouseleave', () => {
|
|
46
|
+
column.classList.remove('hover');
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
column.addEventListener('click', () => handleColumnClick(col));
|
|
50
|
+
|
|
51
|
+
for (let row = 0; row < ROWS; row++) {
|
|
52
|
+
const cell = document.createElement('div');
|
|
53
|
+
cell.className = 'cell';
|
|
54
|
+
cell.dataset.row = row;
|
|
55
|
+
cell.dataset.col = col;
|
|
56
|
+
|
|
57
|
+
const disc = document.createElement('div');
|
|
58
|
+
disc.className = 'disc';
|
|
59
|
+
|
|
60
|
+
const value = gameState.board[row][col];
|
|
61
|
+
if (value === PLAYER1) {
|
|
62
|
+
disc.classList.add('player1');
|
|
63
|
+
} else if (value === PLAYER2) {
|
|
64
|
+
disc.classList.add('player2');
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
cell.appendChild(disc);
|
|
68
|
+
column.appendChild(cell);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
boardElement.appendChild(column);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Handle column click
|
|
76
|
+
async function handleColumnClick(col) {
|
|
77
|
+
if (gameState.gameOver || gameState.isAnimating) return;
|
|
78
|
+
|
|
79
|
+
const row = getLowestEmptyRow(col);
|
|
80
|
+
if (row === -1) return; // Column is full
|
|
81
|
+
|
|
82
|
+
gameState.isAnimating = true;
|
|
83
|
+
|
|
84
|
+
// Place disc with animation
|
|
85
|
+
await placeDisc(row, col, gameState.currentPlayer);
|
|
86
|
+
|
|
87
|
+
// Check for win or draw
|
|
88
|
+
if (checkWin(row, col)) {
|
|
89
|
+
await highlightWinningDiscs();
|
|
90
|
+
endGame(gameState.currentPlayer);
|
|
91
|
+
gameState.isAnimating = false;
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if (isBoardFull()) {
|
|
96
|
+
endGame(0); // Draw
|
|
97
|
+
gameState.isAnimating = false;
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Switch player
|
|
102
|
+
gameState.currentPlayer = gameState.currentPlayer === PLAYER1 ? PLAYER2 : PLAYER1;
|
|
103
|
+
updateTurnIndicator();
|
|
104
|
+
|
|
105
|
+
gameState.isAnimating = false;
|
|
106
|
+
|
|
107
|
+
// Computer's turn
|
|
108
|
+
if (gameState.gameMode === 'pvc' && gameState.currentPlayer === PLAYER2) {
|
|
109
|
+
await computerMove();
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Get lowest empty row in column
|
|
114
|
+
function getLowestEmptyRow(col) {
|
|
115
|
+
for (let row = ROWS - 1; row >= 0; row--) {
|
|
116
|
+
if (gameState.board[row][col] === 0) {
|
|
117
|
+
return row;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
return -1;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// Place disc with animation
|
|
124
|
+
function placeDisc(row, col, player) {
|
|
125
|
+
return new Promise((resolve) => {
|
|
126
|
+
gameState.board[row][col] = player;
|
|
127
|
+
|
|
128
|
+
const cell = document.querySelector(`.cell[data-row="${row}"][data-col="${col}"]`);
|
|
129
|
+
const disc = cell.querySelector('.disc');
|
|
130
|
+
|
|
131
|
+
// Add class for animation
|
|
132
|
+
disc.classList.add(player === PLAYER1 ? 'player1' : 'player2');
|
|
133
|
+
disc.classList.add('dropping');
|
|
134
|
+
|
|
135
|
+
// Calculate drop distance
|
|
136
|
+
const dropDistance = (ROWS - row) * 70; // 70px per row
|
|
137
|
+
disc.style.setProperty('--drop-distance', `${dropDistance}px`);
|
|
138
|
+
|
|
139
|
+
setTimeout(() => {
|
|
140
|
+
disc.classList.remove('dropping');
|
|
141
|
+
resolve();
|
|
142
|
+
}, 500);
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// Check win
|
|
147
|
+
function checkWin(row, col) {
|
|
148
|
+
const player = gameState.board[row][col];
|
|
149
|
+
|
|
150
|
+
// Check horizontal
|
|
151
|
+
if (checkDirection(row, col, 0, 1, player) ||
|
|
152
|
+
checkDirection(row, col, 0, -1, player)) {
|
|
153
|
+
return true;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// Check vertical
|
|
157
|
+
if (checkDirection(row, col, 1, 0, player)) {
|
|
158
|
+
return true;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// Check diagonal /
|
|
162
|
+
if (checkDirection(row, col, 1, 1, player) ||
|
|
163
|
+
checkDirection(row, col, -1, -1, player)) {
|
|
164
|
+
return true;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// Check diagonal \
|
|
168
|
+
if (checkDirection(row, col, 1, -1, player) ||
|
|
169
|
+
checkDirection(row, col, -1, 1, player)) {
|
|
170
|
+
return true;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
return false;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// Check direction
|
|
177
|
+
function checkDirection(row, col, dRow, dCol, player) {
|
|
178
|
+
let count = 1; // Count current disc
|
|
179
|
+
const winningDiscs = [[row, col]];
|
|
180
|
+
|
|
181
|
+
// Check positive direction
|
|
182
|
+
for (let i = 1; i < 4; i++) {
|
|
183
|
+
const newRow = row + dRow * i;
|
|
184
|
+
const newCol = col + dCol * i;
|
|
185
|
+
|
|
186
|
+
if (newRow < 0 || newRow >= ROWS || newCol < 0 || newCol >= COLS) break;
|
|
187
|
+
if (gameState.board[newRow][newCol] !== player) break;
|
|
188
|
+
|
|
189
|
+
count++;
|
|
190
|
+
winningDiscs.push([newRow, newCol]);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// Check negative direction
|
|
194
|
+
for (let i = 1; i < 4; i++) {
|
|
195
|
+
const newRow = row - dRow * i;
|
|
196
|
+
const newCol = col - dCol * i;
|
|
197
|
+
|
|
198
|
+
if (newRow < 0 || newRow >= ROWS || newCol < 0 || newCol >= COLS) break;
|
|
199
|
+
if (gameState.board[newRow][newCol] !== player) break;
|
|
200
|
+
|
|
201
|
+
count++;
|
|
202
|
+
winningDiscs.push([newRow, newCol]);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
if (count >= 4) {
|
|
206
|
+
gameState.winningDiscs = winningDiscs;
|
|
207
|
+
return true;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
return false;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// Highlight winning discs
|
|
214
|
+
function highlightWinningDiscs() {
|
|
215
|
+
return new Promise((resolve) => {
|
|
216
|
+
gameState.winningDiscs.forEach(([row, col]) => {
|
|
217
|
+
const cell = document.querySelector(`.cell[data-row="${row}"][data-col="${col}"]`);
|
|
218
|
+
const disc = cell.querySelector('.disc');
|
|
219
|
+
disc.classList.add('winning');
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
setTimeout(resolve, 500);
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// Check if board is full
|
|
227
|
+
function isBoardFull() {
|
|
228
|
+
return gameState.board[0].every(cell => cell !== 0);
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// Computer move
|
|
232
|
+
async function computerMove() {
|
|
233
|
+
gameState.isAnimating = true;
|
|
234
|
+
|
|
235
|
+
// Add thinking delay
|
|
236
|
+
await new Promise(resolve => setTimeout(resolve, 500));
|
|
237
|
+
|
|
238
|
+
let col;
|
|
239
|
+
|
|
240
|
+
switch (gameState.difficulty) {
|
|
241
|
+
case 'easy':
|
|
242
|
+
col = getRandomMove();
|
|
243
|
+
break;
|
|
244
|
+
case 'medium':
|
|
245
|
+
col = Math.random() < 0.5 ? getBestMove() : getRandomMove();
|
|
246
|
+
break;
|
|
247
|
+
case 'hard':
|
|
248
|
+
col = getBestMove();
|
|
249
|
+
break;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
const row = getLowestEmptyRow(col);
|
|
253
|
+
if (row === -1) {
|
|
254
|
+
gameState.isAnimating = false;
|
|
255
|
+
return computerMove(); // Try again
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
await placeDisc(row, col, PLAYER2);
|
|
259
|
+
|
|
260
|
+
if (checkWin(row, col)) {
|
|
261
|
+
await highlightWinningDiscs();
|
|
262
|
+
endGame(PLAYER2);
|
|
263
|
+
gameState.isAnimating = false;
|
|
264
|
+
return;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
if (isBoardFull()) {
|
|
268
|
+
endGame(0);
|
|
269
|
+
gameState.isAnimating = false;
|
|
270
|
+
return;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
gameState.currentPlayer = PLAYER1;
|
|
274
|
+
updateTurnIndicator();
|
|
275
|
+
gameState.isAnimating = false;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
// Get random move
|
|
279
|
+
function getRandomMove() {
|
|
280
|
+
const availableCols = [];
|
|
281
|
+
for (let col = 0; col < COLS; col++) {
|
|
282
|
+
if (getLowestEmptyRow(col) !== -1) {
|
|
283
|
+
availableCols.push(col);
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
return availableCols[Math.floor(Math.random() * availableCols.length)];
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
// Get best move (simple AI)
|
|
290
|
+
function getBestMove() {
|
|
291
|
+
// Try to win
|
|
292
|
+
for (let col = 0; col < COLS; col++) {
|
|
293
|
+
const row = getLowestEmptyRow(col);
|
|
294
|
+
if (row !== -1) {
|
|
295
|
+
gameState.board[row][col] = PLAYER2;
|
|
296
|
+
if (checkWin(row, col)) {
|
|
297
|
+
gameState.board[row][col] = 0;
|
|
298
|
+
return col;
|
|
299
|
+
}
|
|
300
|
+
gameState.board[row][col] = 0;
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
// Block opponent's win
|
|
305
|
+
for (let col = 0; col < COLS; col++) {
|
|
306
|
+
const row = getLowestEmptyRow(col);
|
|
307
|
+
if (row !== -1) {
|
|
308
|
+
gameState.board[row][col] = PLAYER1;
|
|
309
|
+
if (checkWin(row, col)) {
|
|
310
|
+
gameState.board[row][col] = 0;
|
|
311
|
+
return col;
|
|
312
|
+
}
|
|
313
|
+
gameState.board[row][col] = 0;
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
// Prefer center column
|
|
318
|
+
if (getLowestEmptyRow(3) !== -1) {
|
|
319
|
+
return 3;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
// Random move
|
|
323
|
+
return getRandomMove();
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
// End game
|
|
327
|
+
function endGame(winner) {
|
|
328
|
+
gameState.gameOver = true;
|
|
329
|
+
|
|
330
|
+
if (winner === PLAYER1) {
|
|
331
|
+
gameState.score1++;
|
|
332
|
+
localStorage.setItem('connect4Score1', gameState.score1);
|
|
333
|
+
updateScore();
|
|
334
|
+
showMessage('🎉 שחקן 1 ניצח!', 'player1');
|
|
335
|
+
} else if (winner === PLAYER2) {
|
|
336
|
+
gameState.score2++;
|
|
337
|
+
localStorage.setItem('connect4Score2', gameState.score2);
|
|
338
|
+
updateScore();
|
|
339
|
+
const message = gameState.gameMode === 'pvc' ? '🤖 המחשב ניצח!' : '🎉 שחקן 2 ניצח!';
|
|
340
|
+
showMessage(message, 'player2');
|
|
341
|
+
} else {
|
|
342
|
+
showMessage('🤝 תיקו!', 'draw');
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
// Show message
|
|
347
|
+
function showMessage(text, type) {
|
|
348
|
+
const turnIndicator = document.getElementById('turnIndicator');
|
|
349
|
+
turnIndicator.textContent = text;
|
|
350
|
+
turnIndicator.className = `message ${type}`;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
// Update turn indicator
|
|
354
|
+
function updateTurnIndicator() {
|
|
355
|
+
const turnIndicator = document.getElementById('turnIndicator');
|
|
356
|
+
if (gameState.currentPlayer === PLAYER1) {
|
|
357
|
+
turnIndicator.textContent = 'תור שחקן 1';
|
|
358
|
+
turnIndicator.className = 'player1-turn';
|
|
359
|
+
} else {
|
|
360
|
+
const text = gameState.gameMode === 'pvc' ? 'תור המחשב' : 'תור שחקן 2';
|
|
361
|
+
turnIndicator.textContent = text;
|
|
362
|
+
turnIndicator.className = 'player2-turn';
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
// Update score
|
|
367
|
+
function updateScore() {
|
|
368
|
+
document.getElementById('score1').textContent = gameState.score1;
|
|
369
|
+
document.getElementById('score2').textContent = gameState.score2;
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
// New game
|
|
373
|
+
function newGame() {
|
|
374
|
+
initBoard();
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
// Reset score
|
|
378
|
+
function resetScore() {
|
|
379
|
+
gameState.score1 = 0;
|
|
380
|
+
gameState.score2 = 0;
|
|
381
|
+
localStorage.setItem('connect4Score1', 0);
|
|
382
|
+
localStorage.setItem('connect4Score2', 0);
|
|
383
|
+
updateScore();
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
// Game mode change
|
|
387
|
+
document.querySelectorAll('input[name="gameMode"]').forEach(radio => {
|
|
388
|
+
radio.addEventListener('change', (e) => {
|
|
389
|
+
gameState.gameMode = e.target.value;
|
|
390
|
+
const difficultySelector = document.querySelector('.difficulty-selector');
|
|
391
|
+
|
|
392
|
+
if (gameState.gameMode === 'pvc') {
|
|
393
|
+
difficultySelector.style.display = 'flex';
|
|
394
|
+
} else {
|
|
395
|
+
difficultySelector.style.display = 'none';
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
newGame();
|
|
399
|
+
});
|
|
400
|
+
});
|
|
401
|
+
|
|
402
|
+
// Difficulty change
|
|
403
|
+
document.getElementById('difficulty').addEventListener('change', (e) => {
|
|
404
|
+
gameState.difficulty = e.target.value;
|
|
405
|
+
});
|
|
406
|
+
|
|
407
|
+
// Event listeners
|
|
408
|
+
document.getElementById('newGameBtn').addEventListener('click', newGame);
|
|
409
|
+
document.getElementById('resetScoreBtn').addEventListener('click', resetScore);
|
|
410
|
+
|
|
411
|
+
// Initialize
|
|
412
|
+
updateScore();
|
|
413
|
+
initBoard();
|