anushaacharya's picture
Upload folder using huggingface_hub
4eb7f58 verified
"""Custom web interface for the Minesweeper environment."""
MINESWEEPER_HTML = """<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Minesweeper</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; background: #1a1a2e; color: #eee; min-height: 100vh; display: flex; flex-direction: column; align-items: center; padding: 20px; }
h1 { margin-bottom: 10px; font-size: 24px; }
.status-bar { display: flex; gap: 20px; margin-bottom: 15px; font-size: 14px; color: #aaa; }
.status-bar span { background: #16213e; padding: 6px 14px; border-radius: 6px; }
.status-bar .val { color: #e94560; font-weight: 600; }
#board { display: inline-grid; gap: 2px; background: #16213e; padding: 6px; border-radius: 8px; margin-bottom: 15px; }
.cell { width: 52px; height: 52px; display: flex; align-items: center; justify-content: center; font-size: 18px; font-weight: 700; border-radius: 4px; cursor: pointer; user-select: none; transition: background 0.1s; }
.cell.unrevealed { background: #0f3460; }
.cell.unrevealed:hover { background: #1a5276; }
.cell.revealed { background: #2c3e6d; cursor: default; }
.cell.flag { background: #e94560; }
.cell.mine { background: #c0392b; }
.cell.n1 { color: #3498db; } .cell.n2 { color: #2ecc71; } .cell.n3 { color: #e74c3c; }
.cell.n4 { color: #9b59b6; } .cell.n5 { color: #e67e22; } .cell.n6 { color: #1abc9c; }
.cell.n7 { color: #34495e; } .cell.n8 { color: #95a5a6; }
.controls { display: flex; gap: 10px; margin-bottom: 15px; align-items: center; }
.btn { padding: 10px 24px; border: none; border-radius: 6px; font-size: 14px; font-weight: 600; cursor: pointer; transition: opacity 0.15s; }
.btn:hover { opacity: 0.85; }
.btn-reset { background: #e94560; color: white; }
.btn-mode { background: #0f3460; color: white; min-width: 140px; }
.btn-mode.flagging { background: #e94560; }
#message { font-size: 18px; font-weight: 600; min-height: 28px; margin-bottom: 10px; }
.reward-display { font-size: 13px; color: #888; margin-bottom: 10px; }
.won { color: #2ecc71; }
.lost { color: #e74c3c; }
</style>
</head>
<body>
<h1>Minesweeper</h1>
<div id="message">Click Reset to start</div>
<div class="reward-display" id="reward"></div>
<div class="status-bar">
<span>Mines: <span class="val" id="mines">-</span></span>
<span>Flags: <span class="val" id="flags">-</span></span>
<span>Revealed: <span class="val" id="revealed">-</span></span>
</div>
<div id="board"></div>
<div class="controls">
<button class="btn btn-reset" id="reset-btn">Reset</button>
<button class="btn btn-mode" id="mode-btn">Mode: Reveal</button>
</div>
<script>
let mode = 'reveal';
let gameOver = false;
document.getElementById('reset-btn').addEventListener('click', doReset);
document.getElementById('mode-btn').addEventListener('click', () => {
mode = mode === 'reveal' ? 'flag' : 'reveal';
const btn = document.getElementById('mode-btn');
btn.textContent = 'Mode: ' + (mode === 'reveal' ? 'Reveal' : 'Flag');
btn.classList.toggle('flagging', mode === 'flag');
});
async function doReset() {
gameOver = false;
document.getElementById('message').textContent = 'Resetting...';
document.getElementById('message').className = '';
try {
const r = await fetch('/reset', { method: 'POST', headers: {'Content-Type':'application/json'} });
const data = await r.json();
renderBoard(data);
document.getElementById('message').textContent = 'Click a cell to play!';
} catch(e) { document.getElementById('message').textContent = 'Error: ' + e.message; }
}
async function doStep(row, col) {
if (gameOver) return;
try {
const r = await fetch('/step', {
method: 'POST',
headers: {'Content-Type':'application/json'},
body: JSON.stringify({ action: { row, col, action_type: mode } })
});
const data = await r.json();
renderBoard(data);
} catch(e) { document.getElementById('message').textContent = 'Error: ' + e.message; }
}
function renderBoard(data) {
const obs = data.observation || {};
const board = obs.board || [];
const rows = board.length;
const cols = rows > 0 ? board[0].length : 0;
document.getElementById('mines').textContent = obs.num_mines ?? '-';
document.getElementById('flags').textContent = obs.flags_placed ?? '-';
document.getElementById('revealed').textContent = obs.cells_revealed ?? '-';
if (data.reward !== null && data.reward !== undefined) {
document.getElementById('reward').textContent = 'Last reward: ' + data.reward;
}
const status = obs.game_status;
if (status === 'won') {
document.getElementById('message').textContent = 'You won!';
document.getElementById('message').className = 'won';
gameOver = true;
} else if (status === 'lost') {
document.getElementById('message').textContent = 'Game over - hit a mine!';
document.getElementById('message').className = 'lost';
gameOver = true;
}
const el = document.getElementById('board');
el.style.gridTemplateColumns = 'repeat(' + cols + ', 52px)';
el.innerHTML = '';
for (let r = 0; r < rows; r++) {
for (let c = 0; c < cols; c++) {
const v = board[r][c];
const cell = document.createElement('div');
cell.className = 'cell';
if (v === -1) {
cell.classList.add('unrevealed');
cell.addEventListener('click', () => doStep(r, c));
} else if (v === 'F') {
cell.classList.add('flag');
cell.textContent = '\\u{1f6a9}';
cell.addEventListener('click', () => doStep(r, c));
} else if (v === '*') {
cell.classList.add('mine');
cell.textContent = '\\u{1f4a3}';
} else {
cell.classList.add('revealed');
if (v > 0) { cell.textContent = v; cell.classList.add('n' + v); }
}
el.appendChild(cell);
}
}
}
// Auto-reset on load
doReset();
</script>
</body>
</html>"""