306 lines
8.0 KiB
JavaScript
306 lines
8.0 KiB
JavaScript
window.addEventListener('load', () => {
|
|
minesweeper.init();
|
|
});
|
|
|
|
const minesweeper = {
|
|
|
|
init() {
|
|
this.logic = remoteLogic;
|
|
this.generateBody();
|
|
this.newGame('small');
|
|
},
|
|
|
|
// ------------------------------------ //
|
|
// ----- Basic Body Structure ----- //
|
|
// ------------------------------------ //
|
|
|
|
generateBody() {
|
|
const body = document.body;
|
|
|
|
const container = document.createElement('div');
|
|
container.id = 'container';
|
|
body.appendChild(container);
|
|
|
|
container.append(this.getHeader(), this.getMain(), this.getButtons(), this.getFooter());
|
|
},
|
|
|
|
getHeader() {
|
|
const header = document.createElement('header');
|
|
|
|
const title = document.createElement('div');
|
|
title.id = 'title';
|
|
header.appendChild(title);
|
|
|
|
const heading = document.createElement('h1');
|
|
heading.innerText = 'Minesweeper';
|
|
|
|
const subheading = document.createElement('p');
|
|
subheading.innerText = 'by Elias Fink';
|
|
|
|
title.append(heading, subheading);
|
|
|
|
return header;
|
|
},
|
|
|
|
getMain() {
|
|
const main = document.createElement('main');
|
|
|
|
return main;
|
|
},
|
|
|
|
getButtons() {
|
|
const buttons = document.createElement('div');
|
|
buttons.id = 'buttons';
|
|
|
|
const smallButton = this.createButton('small');
|
|
const mediumButton = this.createButton('medium');
|
|
const largeButton = this.createButton('large');
|
|
|
|
buttons.append(smallButton, mediumButton, largeButton);
|
|
|
|
return buttons;
|
|
},
|
|
|
|
getFooter() {
|
|
const footer = document.createElement('footer');
|
|
|
|
const copyright = document.createElement('span');
|
|
copyright.innerHTML = 'Copyright © 2024 Elias Fink';
|
|
|
|
const webLink = document.createElement('span');
|
|
webLink.innerHTML = '<a href="https://studium.eliasfink.de/it-lab/" target="_blank" rel="noopener noreferrer">Web Version</a>';
|
|
|
|
const gitLink = document.createElement('span');
|
|
gitLink.innerHTML = '<a href="https://github.com/eliasfnk/it-lab.git" target="_blank" rel="noopener noreferrer">Git Repository</a>';
|
|
|
|
footer.append(copyright, webLink, gitLink);
|
|
|
|
return footer;
|
|
},
|
|
|
|
// ---------------------------------- //
|
|
// ----- Buttons and Fields ----- //
|
|
// ---------------------------------- //
|
|
|
|
createButton(type) {
|
|
const button = document.createElement('button');
|
|
button.id = type;
|
|
button.innerText = type;
|
|
|
|
button.addEventListener('click', () => {
|
|
this.newGame(type);
|
|
});
|
|
|
|
return button;
|
|
},
|
|
|
|
generatePlayfield(size) {
|
|
const playfield = document.querySelector('main');
|
|
playfield.innerText = '';
|
|
|
|
for (let row = 0; row < size; row++) {
|
|
for (let col = 0; col < size; col++) {
|
|
playfield.appendChild(this.createCell(row, col));
|
|
}
|
|
}
|
|
},
|
|
|
|
createCell(row, col) {
|
|
const cell = document.createElement('div');
|
|
cell.classList.add('cell', 'covered');
|
|
|
|
cell.dataset.x = col;
|
|
cell.dataset.y = row;
|
|
|
|
cell.style.width = `calc(100% / ${this.size} - 2px)`;
|
|
cell.style.height = `calc(100% / ${this.size} - 2px)`;
|
|
|
|
this.cellEventListeners(cell);
|
|
|
|
return cell;
|
|
},
|
|
|
|
cellEventListeners(cell) {
|
|
cell.addEventListener('click', (event) => {
|
|
this.cellLeftClickHandler(event);
|
|
});
|
|
cell.addEventListener('contextmenu', (event) => {
|
|
this.cellRightClickHandler(event);
|
|
});
|
|
|
|
cell.addEventListener('touchstart', (event) => {
|
|
this.cellTouchStartHandler(event);
|
|
});
|
|
cell.addEventListener('touchend', (event) => {
|
|
this.cellTouchEndHandler(event);
|
|
});
|
|
},
|
|
|
|
// -------------------------- //
|
|
// ----- Game Start ----- //
|
|
// -------------------------- //
|
|
|
|
gameModes: [
|
|
{
|
|
name: 'small',
|
|
size: 9,
|
|
mines: 10
|
|
},
|
|
{
|
|
name: 'medium',
|
|
size: 16,
|
|
mines: 40
|
|
},
|
|
{
|
|
name: 'large',
|
|
size: 24,
|
|
mines: 150
|
|
},
|
|
],
|
|
|
|
async newGame(gameMode) {
|
|
for (const mode of this.gameModes) {
|
|
if (mode.name == gameMode) {
|
|
this.size = mode.size;
|
|
this.mines = mode.mines;
|
|
}
|
|
}
|
|
|
|
this.generatePlayfield(this.size);
|
|
|
|
await this.logic.init(this.size, this.mines);
|
|
},
|
|
|
|
// ------------------------------------ //
|
|
// ----- Click/Touch Handling ----- //
|
|
// ------------------------------------ //
|
|
|
|
async cellLeftClickHandler(event) {
|
|
event.preventDefault();
|
|
|
|
const x = event.target.dataset.x;
|
|
const y = event.target.dataset.y;
|
|
|
|
const result = await this.logic.sweep(x, y);
|
|
|
|
if (result.minehit) {
|
|
this.logic.gameLost(event, result.mines);
|
|
} else {
|
|
this.logic.uncoverCell(x, y, result.minesAround);
|
|
for (const cell of result.emptyCells) {
|
|
this.logic.uncoverCell(cell.x, cell.y, cell.minesAround);
|
|
}
|
|
if (result.userwins) {
|
|
this.logic.gameWon();
|
|
}
|
|
}
|
|
},
|
|
|
|
cellRightClickHandler(event) {
|
|
event.preventDefault();
|
|
|
|
event.target.classList.toggle('symbol-f');
|
|
},
|
|
|
|
cellTouchStartHandler(event) {
|
|
event.preventDefault();
|
|
|
|
this.touchStartTime = new Date().getTime();
|
|
},
|
|
|
|
cellTouchEndHandler(event) {
|
|
event.preventDefault();
|
|
|
|
const touchDuration = new Date().getTime() - this.touchStartTime;
|
|
if (touchDuration < 500) {
|
|
this.cellLeftClickHandler(event);
|
|
} else {
|
|
this.cellRightClickHandler(event);
|
|
}
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// -------------------------------------------------- //
|
|
// -------------------------------------------------- //
|
|
// -------------------------------------------------- //
|
|
|
|
|
|
|
|
|
|
|
|
const remoteLogic = {
|
|
|
|
// ----------------------------- //
|
|
// ----- Field Filling ----- //
|
|
// ----------------------------- //
|
|
|
|
async init(size, mines) {
|
|
this.serverUrl = 'https://www2.hs-esslingen.de/~melcher/it/minesweeper/?';
|
|
const request = `request=init&size=${size}&mines=${mines}&userid=elfiit00`;
|
|
const response = await this.fetchAndDecode(request);
|
|
this.token = response.token;
|
|
},
|
|
|
|
async fetchAndDecode(request) {
|
|
return fetch(this.serverUrl + request).then(response => response.json());
|
|
},
|
|
|
|
// ------------------------------- //
|
|
// ----- Move Processing ----- //
|
|
// ------------------------------- //
|
|
|
|
async sweep(x, y) {
|
|
const request = `request=sweep&token=${this.token}&x=${x}&y=${y}`;
|
|
return this.fetchAndDecode(request);
|
|
},
|
|
|
|
// ------------------------------- //
|
|
// ----- Cell Uncovering ----- //
|
|
// ------------------------------- //
|
|
|
|
getCell(x, y) {
|
|
return document.querySelector(`[data-x="${x}"][data-y="${y}"]`);
|
|
},
|
|
|
|
uncoverCell(x, y, symbol) {
|
|
this.getCell(x, y).classList.remove('covered');
|
|
|
|
if (symbol) {
|
|
this.getCell(x, y).classList.add(`symbol-${symbol}`);
|
|
}
|
|
},
|
|
|
|
// ------------------------ //
|
|
// ----- Game End ----- //
|
|
// ------------------------ //
|
|
|
|
gameLost(event, mines) {
|
|
event.target.id = 'mine-hit';
|
|
for (const m of mines) {
|
|
this.uncoverCell(m.x, m.y, 'm');
|
|
}
|
|
this.displayOverlay('Game Over');
|
|
},
|
|
|
|
gameWon() {
|
|
this.displayOverlay('Victory');
|
|
},
|
|
|
|
displayOverlay(text) {
|
|
const overlay = document.createElement('div');
|
|
overlay.id = 'overlay';
|
|
|
|
const div = document.createElement('div');
|
|
div.innerText = text;
|
|
overlay.appendChild(div);
|
|
|
|
const main = document.querySelector('main');
|
|
main.appendChild(overlay);
|
|
}
|
|
|
|
}; |