game-jewel / index.html
openfree's picture
Upload index.html with huggingface_hub
7b6ac6c verified
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Jewel Match</title>
<style>
body {
margin: 0;
background: #111;
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
font-family: Arial, sans-serif;
}
#gameContainer {
position: relative;
}
canvas {
border: 2px solid #333;
box-shadow: 0 0 20px rgba(0,0,0,0.5);
}
#ui {
position: absolute;
top: 10px;
left: 10px;
color: white;
font-size: 20px;
}
#score {
margin-bottom: 10px;
}
</style>
</head>
<body>
<div id="gameContainer">
<canvas id="gameCanvas"></canvas>
<div id="ui">
<div id="score">Score: 0</div>
<div id="moves">Moves: 30</div>
</div>
</div>
<script>
const canvas = document.getElementById('gameCanvas');
const ctx = canvas.getContext('2d');
const GRID_SIZE = 8;
const CELL_SIZE = 70;
const ANIMATION_SPEED = 0.15;
canvas.width = GRID_SIZE * CELL_SIZE;
canvas.height = GRID_SIZE * CELL_SIZE;
const GEMS = {
RUBY: {color: '#ff0000', highlight: '#ff6666'},
SAPPHIRE: {color: '#0000ff', highlight: '#6666ff'},
EMERALD: {color: '#00ff00', highlight: '#66ff66'},
DIAMOND: {color: '#ffffff', highlight: '#aaaaaa'},
TOPAZ: {color: '#ffff00', highlight: '#ffff66'},
AMETHYST: {color: '#ff00ff', highlight: '#ff66ff'}
};
let score = 0;
let moves = 30;
let board = [];
let selectedGem = null;
let animations = [];
let isAnimating = false;
class Gem {
constructor(type, row, col) {
this.type = type;
this.row = row;
this.col = col;
this.x = col * CELL_SIZE;
this.y = row * CELL_SIZE;
this.targetX = this.x;
this.targetY = this.y;
this.scale = 1;
this.alpha = 1;
}
draw() {
ctx.save();
ctx.globalAlpha = this.alpha;
ctx.translate(this.x + CELL_SIZE/2, this.y + CELL_SIZE/2);
ctx.scale(this.scale, this.scale);
// Draw gem base
ctx.beginPath();
ctx.arc(0, 0, CELL_SIZE/2 - 10, 0, Math.PI * 2);
ctx.fillStyle = GEMS[this.type].color;
ctx.fill();
// Draw highlight
ctx.beginPath();
ctx.arc(-10, -10, 10, 0, Math.PI * 2);
ctx.fillStyle = GEMS[this.type].highlight;
ctx.fill();
ctx.restore();
}
update() {
if(this.x !== this.targetX) {
this.x += (this.targetX - this.x) * ANIMATION_SPEED;
}
if(this.y !== this.targetY) {
this.y += (this.targetY - this.y) * ANIMATION_SPEED;
}
}
}
function initBoard() {
for(let row = 0; row < GRID_SIZE; row++) {
board[row] = [];
for(let col = 0; col < GRID_SIZE; col++) {
const gemTypes = Object.keys(GEMS);
const randomType = gemTypes[Math.floor(Math.random() * gemTypes.length)];
board[row][col] = new Gem(randomType, row, col);
}
}
}
function draw() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
// Draw board
for(let row = 0; row < GRID_SIZE; row++) {
for(let col = 0; col < GRID_SIZE; col++) {
if(board[row][col]) {
board[row][col].draw();
}
}
}
// Draw selected gem highlight
if(selectedGem) {
ctx.strokeStyle = '#fff';
ctx.lineWidth = 3;
ctx.strokeRect(
selectedGem.col * CELL_SIZE,
selectedGem.row * CELL_SIZE,
CELL_SIZE,
CELL_SIZE
);
}
requestAnimationFrame(draw);
}
function update() {
for(let row = 0; row < GRID_SIZE; row++) {
for(let col = 0; col < GRID_SIZE; col++) {
if(board[row][col]) {
board[row][col].update();
}
}
}
}
function handleClick(e) {
if(isAnimating) return;
const rect = canvas.getBoundingClientRect();
const x = e.clientX - rect.left;
const y = e.clientY - rect.top;
const col = Math.floor(x / CELL_SIZE);
const row = Math.floor(y / CELL_SIZE);
if(col >= 0 && col < GRID_SIZE && row >= 0 && row < GRID_SIZE) {
if(!selectedGem) {
selectedGem = board[row][col];
} else {
// Check if adjacent
const rowDiff = Math.abs(selectedGem.row - row);
const colDiff = Math.abs(selectedGem.col - col);
if((rowDiff === 1 && colDiff === 0) || (rowDiff === 0 && colDiff === 1)) {
swapGems(selectedGem, board[row][col]);
}
selectedGem = null;
}
}
}
function swapGems(gem1, gem2) {
const tempX = gem1.targetX;
const tempY = gem1.targetY;
const tempRow = gem1.row;
const tempCol = gem1.col;
gem1.targetX = gem2.targetX;
gem1.targetY = gem2.targetY;
gem1.row = gem2.row;
gem1.col = gem2.col;
gem2.targetX = tempX;
gem2.targetY = tempY;
gem2.row = tempRow;
gem2.col = tempCol;
board[gem1.row][gem1.col] = gem1;
board[gem2.row][gem2.col] = gem2;
moves--;
document.getElementById('moves').textContent = `Moves: ${moves}`;
setTimeout(checkMatches, 300);
}
function checkMatches() {
let matches = [];
// Check horizontal matches
for(let row = 0; row < GRID_SIZE; row++) {
let matchCount = 1;
let matchType = board[row][0]?.type;
for(let col = 1; col < GRID_SIZE; col++) {
if(board[row][col]?.type === matchType) {
matchCount++;
} else {
if(matchCount >= 3) {
for(let i = col-matchCount; i < col; i++) {
matches.push({row, col: i});
}
}
matchCount = 1;
matchType = board[row][col]?.type;
}
}
if(matchCount >= 3) {
for(let i = GRID_SIZE-matchCount; i < GRID_SIZE; i++) {
matches.push({row, col: i});
}
}
}
// Check vertical matches
for(let col = 0; col < GRID_SIZE; col++) {
let matchCount = 1;
let matchType = board[0][col]?.type;
for(let row = 1; row < GRID_SIZE; row++) {
if(board[row][col]?.type === matchType) {
matchCount++;
} else {
if(matchCount >= 3) {
for(let i = row-matchCount; i < row; i++) {
matches.push({row: i, col});
}
}
matchCount = 1;
matchType = board[row][col]?.type;
}
}
if(matchCount >= 3) {
for(let i = GRID_SIZE-matchCount; i < GRID_SIZE; i++) {
matches.push({row: i, col});
}
}
}
if(matches.length > 0) {
removeMatches(matches);
}
}
function removeMatches(matches) {
isAnimating = true;
// Remove matched gems
matches.forEach(({row, col}) => {
if(board[row][col]) {
score += 10;
document.getElementById('score').textContent = `Score: ${score}`;
board[row][col] = null;
}
});
// Drop remaining gems
for(let col = 0; col < GRID_SIZE; col++) {
let emptySpaces = 0;
for(let row = GRID_SIZE-1; row >= 0; row--) {
if(!board[row][col]) {
emptySpaces++;
} else if(emptySpaces > 0) {
const gem = board[row][col];
gem.row += emptySpaces;
gem.targetY = gem.row * CELL_SIZE;
board[gem.row][col] = gem;
board[row][col] = null;
}
}
// Fill empty spaces with new gems
for(let row = emptySpaces-1; row >= 0; row--) {
const gemTypes = Object.keys(GEMS);
const randomType = gemTypes[Math.floor(Math.random() * gemTypes.length)];
const newGem = new Gem(randomType, row, col);
newGem.y = -CELL_SIZE;
newGem.targetY = row * CELL_SIZE;
board[row][col] = newGem;
}
}
setTimeout(() => {
isAnimating = false;
checkMatches();
}, 500);
}
canvas.addEventListener('click', handleClick);
function gameLoop() {
update();
requestAnimationFrame(gameLoop);
}
initBoard();
gameLoop();
draw();
</script>
</body>
</html>