Skip to content

ajcwebdev/chase-maze

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

3 Commits
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Chase Maze

HTML

<!DOCTYPE html>
<html>
<head>
    <title>Chase Maze</title>
    <link rel="stylesheet" href="styles.css">
</head>
<body>
    <div class="container">
        <h1>Chase Maze</h1>
        <div class="stats">
            <div id="score1">P1 Score: 0</div>
            <div id="score2">P2 Score: 0</div>
            <div id="timer">Time: 0:00</div>
            <div id="countdown">Next maze in: 60s</div>
        </div>
        <div id="maze"></div>
        <div class="instructions">
            <p>Player 1: Arrow Keys | Player 2: WASD</p>
            <p>Catch the moving red square!</p>
            <p>Maze changes each catch!</p>
            <p>Press Space for manual reset</p>
        </div>
    </div>
    <script src="game.js"></script>
</body>
</html>

CSS

.container {
    display: flex;
    flex-direction: column;
    align-items: center;
    font-family: Arial, sans-serif;
    padding: 20px;
}

#maze {
    background: #f0f0f0;
    padding: 10px;
    border-radius: 5px;
    transition: opacity 0.3s;
}

.maze-row {
    display: flex;
}

.cell {
    width: 30px;
    height: 30px;
    border: 1px solid #ddd;
    display: flex;
    align-items: center;
    justify-content: center;
}

.wall {
    background-color: #333;
}

.player1 {
    background-color: #4CAF50;
    border-radius: 50%;
}

.player2 {
    background-color: #2196F3;
    border-radius: 50%;
}

.goal {
    background-color: #f44336;
    border-radius: 4px;
}

.instructions {
    margin: 20px;
    padding: 15px;
    background: #e9e9e9;
    border-radius: 5px;
}

.stats {
    display: flex;
    gap: 20px;
    font-size: 24px;
    margin: 10px;
}

#score1 {
    color: #4CAF50;
    font-weight: bold;
}

#score2 {
    color: #2196F3;
    font-weight: bold;
}

.countdown {
    color: #f44336;
    font-weight: bold;
}

JavaScript

// game.js

const state = {
    width: 15,
    height: 15,
    maze: [],
    player1: { x: 1, y: 1 },
    player2: { x: 13, y: 1 },
    goal: { x: 7, y: 7 },
    moveInterval1: null,
    moveInterval2: null,
    goalMoveInterval: null,
    moveSpeed: 100,
    goalSpeed: 200,
    keysPressed: new Set(),
    score1: 0,
    score2: 0,
    startTime: Date.now(),
    timerInterval: null,
    nextResetTime: Date.now() + 60000
}

const initMaze = () => {
    state.maze = Array(state.height).fill().map(() => Array(state.width).fill(1))
}

const generateMaze = () => {
    const stack = [{x: 1, y: 1}]
    state.maze[1][1] = 0

    while (stack.length > 0) {
        const current = stack[stack.length - 1]
        const neighbors = []
        const directions = [[0, -2], [2, 0], [0, 2], [-2, 0]]

        for (let [dx, dy] of directions) {
            const newX = current.x + dx
            const newY = current.y + dy
            if (newX > 0 && newX < state.width - 1 && 
                newY > 0 && newY < state.height - 1 && 
                state.maze[newY][newX] === 1) {
                neighbors.push({x: newX, y: newY, dx, dy})
            }
        }

        if (neighbors.length === 0) {
            stack.pop()
        } else {
            const next = neighbors[Math.floor(Math.random() * neighbors.length)]
            state.maze[next.y][next.x] = 0
            state.maze[current.y + next.dy/2][current.x + next.dx/2] = 0
            stack.push({x: next.x, y: next.y})
        }
    }

    // Open center area
    for (let y = 6; y < 9; y++) {
        for (let x = 6; x < 9; x++) {
            state.maze[y][x] = 0
        }
    }
}

const moveGoal = () => {
    const moves = []
    const directions = [[0, -1], [1, 0], [0, 1], [-1, 0]]

    for (let [dx, dy] of directions) {
        const newX = state.goal.x + dx
        const newY = state.goal.y + dy
        if (newX >= 0 && newX < state.width && 
            newY >= 0 && newY < state.height && 
            state.maze[newY][newX] === 0) {
            const dist1 = Math.abs(newX - state.player1.x) + Math.abs(newY - state.player1.y)
            const dist2 = Math.abs(newX - state.player2.x) + Math.abs(newY - state.player2.y)
            moves.push({x: newX, y: newY, minDist: Math.min(dist1, dist2)})
        }
    }

    if (moves.length === 0) return

    if (Math.random() < 0.7) {
        // Move away from closest player
        moves.sort((a, b) => b.minDist - a.minDist)
        const bestMoves = moves.filter(m => m.minDist === moves[0].minDist)
        const move = bestMoves[Math.floor(Math.random() * bestMoves.length)]
        state.goal.x = move.x
        state.goal.y = move.y
    } else {
        // Random move
        const move = moves[Math.floor(Math.random() * moves.length)]
        state.goal.x = move.x
        state.goal.y = move.y
    }
}

const spawnGoal = () => {
    const centerX = Math.floor(state.width / 2)
    const centerY = Math.floor(state.height / 2)
    const validSpots = []

    for (let y = centerY - 2; y <= centerY + 2; y++) {
        for (let x = centerX - 2; x <= centerX + 2; x++) {
            if (state.maze[y][x] === 0) {
                validSpots.push({x, y})
            }
        }
    }

    if (validSpots.length > 0) {
        const spot = validSpots[Math.floor(Math.random() * validSpots.length)]
        state.goal.x = spot.x
        state.goal.y = spot.y
    }
}

const render = () => {
    const mazeElement = document.getElementById('maze')
    mazeElement.innerHTML = ''

    for (let y = 0; y < state.height; y++) {
        const row = document.createElement('div')
        row.className = 'maze-row'

        for (let x = 0; x < state.width; x++) {
            const cell = document.createElement('div')
            cell.className = 'cell'
            if (state.maze[y][x] === 1) cell.classList.add('wall')
            if (x === state.player1.x && y === state.player1.y) cell.classList.add('player1')
            if (x === state.player2.x && y === state.player2.y) cell.classList.add('player2')
            if (x === state.goal.x && y === state.goal.y) cell.classList.add('goal')
            row.appendChild(cell)
        }

        mazeElement.appendChild(row)
    }

    document.getElementById('score1').textContent = `P1 Score: ${state.score1}`
    document.getElementById('score2').textContent = `P2 Score: ${state.score2}`
}

const updateTimer = () => {
    const elapsed = Math.floor((Date.now() - state.startTime) / 1000)
    const minutes = Math.floor(elapsed / 60)
    const seconds = elapsed % 60
    document.getElementById('timer').textContent = 
        `Time: ${minutes}:${seconds.toString().padStart(2, '0')}`
}

const updateCountdown = () => {
    const remaining = Math.ceil((state.nextResetTime - Date.now()) / 1000)
    document.getElementById('countdown').textContent = `Next maze in: ${remaining}s`
    document.getElementById('countdown').classList.toggle('countdown', remaining <= 10)
}

const regenerateMaze = () => {
    const mazeElement = document.getElementById('maze')
    mazeElement.style.opacity = '0'

    setTimeout(() => {
        initMaze()
        generateMaze()
        spawnGoal()
        mazeElement.style.opacity = '1'
        render()
    }, 300)
}

const movePlayer = (playerNum, dx, dy) => {
    const player = playerNum === 1 ? state.player1 : state.player2
    const newX = player.x + dx
    const newY = player.y + dy

    if (newX >= 0 && newX < state.width && 
        newY >= 0 && newY < state.height && 
        state.maze[newY][newX] === 0) {
        player.x = newX
        player.y = newY

        if (player.x === state.goal.x && player.y === state.goal.y) {
            if (playerNum === 1) state.score1++
            else state.score2++
            state.goalSpeed = Math.max(100, state.goalSpeed - 10)
            regenerateMaze()
        }
        render()
    }
}

const startMovement = (playerNum, dx, dy) => {
    const intervalKey = `moveInterval${playerNum}`
    if (state[intervalKey]) clearInterval(state[intervalKey])
    movePlayer(playerNum, dx, dy)
    state[intervalKey] = setInterval(() => movePlayer(playerNum, dx, dy), state.moveSpeed)
}

const stopMovement = (playerNum) => {
    const intervalKey = `moveInterval${playerNum}`
    if (state[intervalKey]) {
        clearInterval(state[intervalKey])
        state[intervalKey] = null
    }
}

const startGame = () => {
    state.goalMoveInterval = setInterval(() => {
        moveGoal()
        render()
    }, state.goalSpeed)

    state.timerInterval = setInterval(() => {
        updateTimer()
        updateCountdown()
        if (Date.now() >= state.nextResetTime) {
            state.nextResetTime = Date.now() + 60000
            regenerateMaze()
        }
    }, 1000)
}

const reset = () => {
    clearInterval(state.moveInterval1)
    clearInterval(state.moveInterval2)
    clearInterval(state.goalMoveInterval)
    clearInterval(state.timerInterval)

    state.player1 = { x: 1, y: 1 }
    state.player2 = { x: 13, y: 1 }
    state.score1 = 0
    state.score2 = 0
    state.goalSpeed = 200
    state.keysPressed.clear()
    state.startTime = Date.now()
    state.nextResetTime = Date.now() + 60000

    regenerateMaze()
    startGame()
}

document.addEventListener('keydown', (e) => {
    if (state.keysPressed.has(e.key)) return
    state.keysPressed.add(e.key)

    switch (e.key.toLowerCase()) {
        case 'arrowup': startMovement(1, 0, -1); break
        case 'arrowdown': startMovement(1, 0, 1); break
        case 'arrowleft': startMovement(1, -1, 0); break
        case 'arrowright': startMovement(1, 1, 0); break
        case 'w': startMovement(2, 0, -1); break
        case 's': startMovement(2, 0, 1); break
        case 'a': startMovement(2, -1, 0); break
        case 'd': startMovement(2, 1, 0); break
        case ' ': reset(); break
    }
})

document.addEventListener('keyup', (e) => {
    state.keysPressed.delete(e.key)
    const key = e.key.toLowerCase()
    if (['arrowup', 'arrowdown', 'arrowleft', 'arrowright'].includes(key)) stopMovement(1)
    if (['w', 'a', 's', 'd'].includes(key)) stopMovement(2)
})

// Start game
initMaze()
generateMaze()
spawnGoal()
render()
startGame()

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published