improved javascript structure, added load selection and made chessboard selectable
This commit is contained in:
+256
-131
@@ -1,53 +1,95 @@
|
||||
let mode = "queens"; // oder "rooks"
|
||||
const DEFAULT_BOARD_SIZE = 8;
|
||||
const MIN_BOARD_SIZE = 4;
|
||||
const MAX_BOARD_SIZE = 13;
|
||||
const EMPTY_CELL = -1;
|
||||
|
||||
let mode = "queens";
|
||||
let boardSize = DEFAULT_BOARD_SIZE;
|
||||
let pendingBoardSize = DEFAULT_BOARD_SIZE;
|
||||
let board = createEmptyBoard(boardSize);
|
||||
|
||||
const boardDiv = document.getElementById("board");
|
||||
const statusMessage = document.getElementById("statusMessage");
|
||||
const queenList = document.getElementById("queenList");
|
||||
const btnQueens = document.getElementById("btnQueens");
|
||||
const btnRooks = document.getElementById("btnRooks");
|
||||
const settingsSidebar = document.getElementById("settingsSidebar");
|
||||
const sidebarBackdrop = document.getElementById("sidebarBackdrop");
|
||||
const savePointsSidebar = document.getElementById("savePointsSidebar");
|
||||
const savePointsBackdrop = document.getElementById("savePointsBackdrop");
|
||||
const boardSizeInput = document.getElementById("boardSizeInput");
|
||||
const boardSizeValue = document.getElementById("boardSizeValue");
|
||||
const boardSizeValueMirror = document.getElementById("boardSizeValueMirror");
|
||||
const sidebarBoardSizeValue = document.getElementById("sidebarBoardSizeValue");
|
||||
const sidebarBoardSizeValueMirror = document.getElementById("sidebarBoardSizeValueMirror");
|
||||
const savePointsList = document.getElementById("savePointsList");
|
||||
|
||||
function createEmptyBoard(size) {
|
||||
return Array(size).fill(EMPTY_CELL);
|
||||
}
|
||||
|
||||
function clampBoardSize(size) {
|
||||
return Math.min(MAX_BOARD_SIZE, Math.max(MIN_BOARD_SIZE, Number(size)));
|
||||
}
|
||||
|
||||
function getColumnLabel(index) {
|
||||
return String.fromCharCode(65 + index);
|
||||
}
|
||||
|
||||
function updateModeButtons() {
|
||||
btnQueens.classList.toggle("active", mode === "queens");
|
||||
btnRooks.classList.toggle("active", mode === "rooks");
|
||||
}
|
||||
|
||||
function updateBoardSizeDisplays(size) {
|
||||
boardSizeValue.textContent = size;
|
||||
boardSizeValueMirror.textContent = size;
|
||||
sidebarBoardSizeValue.textContent = size;
|
||||
sidebarBoardSizeValueMirror.textContent = size;
|
||||
boardSizeInput.value = size;
|
||||
}
|
||||
|
||||
function setStatus(message = "", color = "") {
|
||||
statusMessage.textContent = message;
|
||||
statusMessage.style.color = color;
|
||||
}
|
||||
|
||||
function updateBoardLayout() {
|
||||
const cellSize = Math.max(32, Math.min(60, Math.floor(480 / boardSize)));
|
||||
|
||||
boardDiv.style.setProperty("--board-size", boardSize);
|
||||
boardDiv.style.setProperty("--cell-size", `${cellSize}px`);
|
||||
}
|
||||
|
||||
function setMode(newMode) {
|
||||
mode = newMode;
|
||||
resetBoard();
|
||||
|
||||
document.getElementById("btnQueens").classList.remove("active");
|
||||
document.getElementById("btnRooks").classList.remove("active");
|
||||
|
||||
if (mode === "queens") {
|
||||
document.getElementById("btnQueens").classList.add("active");
|
||||
} else {
|
||||
document.getElementById("btnRooks").classList.add("active");
|
||||
}
|
||||
|
||||
drawBoard();
|
||||
updateModeButtons();
|
||||
}
|
||||
|
||||
let board = Array(8).fill(-1);
|
||||
const boardDiv = document.getElementById("board");
|
||||
|
||||
function getConflicts() {
|
||||
let conflicts = [];
|
||||
const conflicts = [];
|
||||
|
||||
for (let r1 = 0; r1 < 8; r1++) {
|
||||
if (board[r1] === -1) continue;
|
||||
for (let rowA = 0; rowA < boardSize; rowA++) {
|
||||
if (board[rowA] === EMPTY_CELL) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (let r2 = r1 + 1; r2 < 8; r2++) {
|
||||
if (board[r2] === -1) continue;
|
||||
|
||||
let c1 = board[r1];
|
||||
let c2 = board[r2];
|
||||
|
||||
// 🔴 Türme: nur gleiche Spalte
|
||||
if (mode === "rooks") {
|
||||
if (c1 === c2) {
|
||||
conflicts.push([r1, c1]);
|
||||
conflicts.push([r2, c2]);
|
||||
}
|
||||
for (let rowB = rowA + 1; rowB < boardSize; rowB++) {
|
||||
if (board[rowB] === EMPTY_CELL) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// 🔴 Damen: Spalte + Diagonale
|
||||
else if (mode === "queens") {
|
||||
if (
|
||||
c1 === c2 ||
|
||||
Math.abs(c1 - c2) === Math.abs(r1 - r2)
|
||||
) {
|
||||
conflicts.push([r1, c1]);
|
||||
conflicts.push([r2, c2]);
|
||||
}
|
||||
const colA = board[rowA];
|
||||
const colB = board[rowB];
|
||||
const sameColumn = colA === colB;
|
||||
const sameDiagonal = Math.abs(colA - colB) === Math.abs(rowA - rowB);
|
||||
|
||||
// Rooks only attack vertically, queens also attack diagonally.
|
||||
const hasConflict = mode === "rooks" ? sameColumn : sameColumn || sameDiagonal;
|
||||
|
||||
if (hasConflict) {
|
||||
conflicts.push([rowA, colA], [rowB, colB]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -56,71 +98,48 @@ function getConflicts() {
|
||||
}
|
||||
|
||||
function checkSolution() {
|
||||
const status = document.getElementById("statusMessage");
|
||||
|
||||
const allPlaced = board.every(col => col !== -1);
|
||||
const allPlaced = board.every((col) => col !== EMPTY_CELL);
|
||||
|
||||
if (!allPlaced) {
|
||||
status.textContent = "❗ Noch nicht alle Figuren platziert.";
|
||||
status.style.color = "orange";
|
||||
setStatus("❗ Noch nicht alle Figuren platziert.", "orange");
|
||||
return;
|
||||
}
|
||||
|
||||
const conflicts = getConflicts();
|
||||
|
||||
if (conflicts.length > 0) {
|
||||
status.textContent = "❌ Es gibt Konflikte!";
|
||||
status.style.color = "red";
|
||||
if (getConflicts().length > 0) {
|
||||
setStatus("❌ Es gibt Konflikte!", "red");
|
||||
return;
|
||||
}
|
||||
|
||||
status.textContent = "🎉 Perfekt gelöst!";
|
||||
status.style.color = "green";
|
||||
setStatus("🎉 Perfekt geloest!", "green");
|
||||
}
|
||||
|
||||
function drawBoard() {
|
||||
boardDiv.innerHTML = "";
|
||||
boardDiv.className = "board-grid";
|
||||
updateBoardLayout();
|
||||
|
||||
const conflicts = getConflicts();
|
||||
const letters = ["A", "B", "C", "D", "E", "F", "G", "H"];
|
||||
|
||||
for (let row = -1; row < 8; row++) {
|
||||
for (let col = -1; col < 8; col++) {
|
||||
|
||||
for (let row = -1; row < boardSize; row++) {
|
||||
for (let col = -1; col < boardSize; col++) {
|
||||
const cell = document.createElement("div");
|
||||
|
||||
// 🔹 Ecke oben links
|
||||
if (row === -1 && col === -1) {
|
||||
cell.classList.add("label");
|
||||
}
|
||||
|
||||
// 🔹 Buchstaben (oben)
|
||||
else if (row === -1) {
|
||||
cell.textContent = letters[col];
|
||||
} else if (row === -1) {
|
||||
cell.textContent = getColumnLabel(col);
|
||||
cell.classList.add("label");
|
||||
}
|
||||
|
||||
// 🔹 Zahlen (links)
|
||||
else if (col === -1) {
|
||||
cell.textContent = 8 - row;
|
||||
} else if (col === -1) {
|
||||
cell.textContent = boardSize - row;
|
||||
cell.classList.add("label");
|
||||
}
|
||||
|
||||
// 🔹 echtes Spielfeld
|
||||
else {
|
||||
cell.classList.add("cell");
|
||||
|
||||
if ((row + col) % 2 === 0) {
|
||||
cell.classList.add("white");
|
||||
} else {
|
||||
cell.classList.add("black");
|
||||
}
|
||||
} else {
|
||||
cell.classList.add("cell", (row + col) % 2 === 0 ? "white" : "black");
|
||||
|
||||
if (board[row] === col) {
|
||||
cell.textContent = (mode === "queens") ? "♛" : "♜";
|
||||
cell.textContent = mode === "queens" ? "♛" : "♜";
|
||||
|
||||
if (conflicts.some(([r, c]) => r === row && c === col)) {
|
||||
// Highlight pieces that are part of at least one conflict pair.
|
||||
if (conflicts.some(([conflictRow, conflictCol]) => conflictRow === row && conflictCol === col)) {
|
||||
cell.classList.add("invalid");
|
||||
}
|
||||
}
|
||||
@@ -136,65 +155,163 @@ function drawBoard() {
|
||||
}
|
||||
|
||||
function placeQueen(row, col) {
|
||||
if (board[row] === col) {
|
||||
board[row] = -1;
|
||||
} else {
|
||||
board[row] = col;
|
||||
}
|
||||
board[row] = board[row] === col ? EMPTY_CELL : col;
|
||||
drawBoard();
|
||||
updateQueenList();
|
||||
}
|
||||
|
||||
async function solve() {
|
||||
function previewBoardSize(size) {
|
||||
pendingBoardSize = clampBoardSize(size);
|
||||
sidebarBoardSizeValue.textContent = pendingBoardSize;
|
||||
sidebarBoardSizeValueMirror.textContent = pendingBoardSize;
|
||||
}
|
||||
|
||||
if (mode === "rooks") {
|
||||
const res = await fetch("/solve_rooks");
|
||||
const solution = await res.json();
|
||||
function applyBoardSize() {
|
||||
boardSize = pendingBoardSize;
|
||||
board = createEmptyBoard(boardSize);
|
||||
updateBoardSizeDisplays(boardSize);
|
||||
setStatus(`Brettgroesse auf ${boardSize} x ${boardSize} gesetzt.`, "#2563eb");
|
||||
drawBoard();
|
||||
toggleSidebar(false);
|
||||
}
|
||||
|
||||
board = solution;
|
||||
drawBoard();
|
||||
function toggleSidebar(forceOpen) {
|
||||
const shouldOpen = typeof forceOpen === "boolean" ? forceOpen : !settingsSidebar.classList.contains("open");
|
||||
|
||||
if (shouldOpen) {
|
||||
toggleSavePointsPanel(false);
|
||||
}
|
||||
|
||||
settingsSidebar.classList.toggle("open", shouldOpen);
|
||||
sidebarBackdrop.classList.toggle("visible", shouldOpen);
|
||||
settingsSidebar.setAttribute("aria-hidden", String(!shouldOpen));
|
||||
}
|
||||
|
||||
function toggleSavePointsPanel(forceOpen) {
|
||||
const shouldOpen = typeof forceOpen === "boolean" ? forceOpen : !savePointsSidebar.classList.contains("open");
|
||||
|
||||
savePointsSidebar.classList.toggle("open", shouldOpen);
|
||||
savePointsBackdrop.classList.toggle("visible", shouldOpen);
|
||||
savePointsSidebar.setAttribute("aria-hidden", String(!shouldOpen));
|
||||
}
|
||||
|
||||
function getPieceLabel(selectedMode) {
|
||||
return selectedMode === "queens" ? "Dame" : "Turm";
|
||||
}
|
||||
|
||||
function getBoardPreview(savedBoard, limit = 4) {
|
||||
const preview = [];
|
||||
|
||||
for (let row = 0; row < savedBoard.length; row++) {
|
||||
if (savedBoard[row] === EMPTY_CELL) {
|
||||
continue;
|
||||
}
|
||||
|
||||
preview.push(`${getColumnLabel(savedBoard[row])}${savedBoard.length - row}`);
|
||||
|
||||
if (preview.length === limit) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return preview.length > 0 ? preview.join(", ") : "Noch keine Figuren gesetzt";
|
||||
}
|
||||
|
||||
function renderSavePoints(savePoints) {
|
||||
savePointsList.innerHTML = "";
|
||||
|
||||
if (savePoints.length === 0) {
|
||||
savePointsList.innerHTML = '<p class="sidebar-hint">Noch keine Speicherstaende gefunden.</p>';
|
||||
return;
|
||||
}
|
||||
|
||||
// Damen (wie vorher)
|
||||
const res = await fetch("/solve");
|
||||
const solution = await res.json();
|
||||
for (const savePoint of savePoints) {
|
||||
const item = document.createElement("article");
|
||||
const title = document.createElement("h4");
|
||||
const createdAt = document.createElement("p");
|
||||
const boardMeta = document.createElement("p");
|
||||
const preview = document.createElement("p");
|
||||
const loadButton = document.createElement("button");
|
||||
|
||||
board = Array.isArray(solution[0]) ? solution[0] : solution;
|
||||
item.className = "save-point-card";
|
||||
title.textContent = `Save Point #${savePoint.id}`;
|
||||
createdAt.className = "save-point-meta";
|
||||
createdAt.textContent = `Gespeichert: ${savePoint.created_at}`;
|
||||
boardMeta.className = "save-point-meta";
|
||||
boardMeta.textContent = `${getPieceLabel(savePoint.mode)} | ${savePoint.board_size} x ${savePoint.board_size} | ${savePoint.pieces_placed}/${savePoint.board_size} gesetzt`;
|
||||
preview.className = "save-point-preview";
|
||||
preview.textContent = `Vorschau: ${getBoardPreview(savePoint.board)}`;
|
||||
|
||||
loadButton.className = "btn";
|
||||
loadButton.type = "button";
|
||||
loadButton.textContent = "Diesen laden";
|
||||
loadButton.onclick = () => loadSavePoint(savePoint.id);
|
||||
|
||||
item.appendChild(title);
|
||||
item.appendChild(createdAt);
|
||||
item.appendChild(boardMeta);
|
||||
item.appendChild(preview);
|
||||
item.appendChild(loadButton);
|
||||
savePointsList.appendChild(item);
|
||||
}
|
||||
}
|
||||
|
||||
async function refreshSavePoints() {
|
||||
const response = await fetch("/save_points");
|
||||
const savePoints = await response.json();
|
||||
renderSavePoints(savePoints);
|
||||
}
|
||||
|
||||
async function openSavePointsPanel() {
|
||||
await refreshSavePoints();
|
||||
toggleSidebar(false);
|
||||
toggleSavePointsPanel(true);
|
||||
}
|
||||
|
||||
function applyLoadedGame(data) {
|
||||
board = data.board;
|
||||
boardSize = Array.isArray(data.board) ? clampBoardSize(data.board.length) : DEFAULT_BOARD_SIZE;
|
||||
pendingBoardSize = boardSize;
|
||||
mode = data.mode;
|
||||
|
||||
updateModeButtons();
|
||||
updateBoardSizeDisplays(boardSize);
|
||||
drawBoard();
|
||||
}
|
||||
|
||||
async function solve() {
|
||||
const endpoint = mode === "rooks" ? "/solve_rooks" : "/solve";
|
||||
const response = await fetch(`${endpoint}?size=${boardSize}`);
|
||||
const solution = await response.json();
|
||||
|
||||
// The queens endpoint may return multiple solutions; we display the first one.
|
||||
board = mode === "queens" && Array.isArray(solution[0]) ? solution[0] : solution;
|
||||
setStatus(`Loesung fuer ${boardSize} x ${boardSize} geladen.`, "green");
|
||||
drawBoard();
|
||||
}
|
||||
|
||||
function resetBoard() {
|
||||
const status = document.getElementById("statusMessage");
|
||||
board = Array(8).fill(-1);
|
||||
board = createEmptyBoard(boardSize);
|
||||
setStatus();
|
||||
drawBoard();
|
||||
updateQueenList();
|
||||
status.textContent = "";
|
||||
}
|
||||
|
||||
drawBoard();
|
||||
|
||||
function updateQueenList() {
|
||||
const list = document.getElementById("queenList");
|
||||
list.innerHTML = "";
|
||||
|
||||
const letters = ["A", "B", "C", "D", "E", "F", "G", "H"];
|
||||
queenList.innerHTML = "";
|
||||
|
||||
let count = 1;
|
||||
|
||||
for (let row = 0; row < 8; row++) {
|
||||
if (board[row] !== -1) {
|
||||
let col = board[row];
|
||||
|
||||
let pos = letters[col] + (8 - row);
|
||||
|
||||
const li = document.createElement("li");
|
||||
li.textContent = `${mode === "queens" ? "Dame" : "Turm"} ${count}: ${pos}`;
|
||||
|
||||
list.appendChild(li);
|
||||
count++;
|
||||
for (let row = 0; row < boardSize; row++) {
|
||||
if (board[row] === EMPTY_CELL) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const col = board[row];
|
||||
const position = `${getColumnLabel(col)}${boardSize - row}`;
|
||||
const li = document.createElement("li");
|
||||
|
||||
li.textContent = `${mode === "queens" ? "Dame" : "Turm"} ${count}: ${position}`;
|
||||
queenList.appendChild(li);
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -205,30 +322,38 @@ async function saveGame() {
|
||||
"Content-Type": "application/json"
|
||||
},
|
||||
body: JSON.stringify({
|
||||
board: board,
|
||||
mode: mode
|
||||
board,
|
||||
mode,
|
||||
boardSize
|
||||
})
|
||||
});
|
||||
|
||||
await refreshSavePoints();
|
||||
alert("💾 Spiel gespeichert!");
|
||||
}
|
||||
|
||||
async function loadGame() {
|
||||
const res = await fetch("/load");
|
||||
const data = await res.json();
|
||||
const response = await fetch("/load");
|
||||
const data = await response.json();
|
||||
applyLoadedGame(data);
|
||||
}
|
||||
|
||||
board = data.board;
|
||||
mode = data.mode;
|
||||
async function loadSavePoint(savePointId) {
|
||||
const response = await fetch(`/load/${savePointId}`);
|
||||
|
||||
// Buttons richtig setzen
|
||||
document.getElementById("btnQueens").classList.remove("active");
|
||||
document.getElementById("btnRooks").classList.remove("active");
|
||||
|
||||
if (mode === "queens") {
|
||||
document.getElementById("btnQueens").classList.add("active");
|
||||
} else {
|
||||
document.getElementById("btnRooks").classList.add("active");
|
||||
if (!response.ok) {
|
||||
setStatus("❌ Dieser Speicherstand konnte nicht geladen werden.", "red");
|
||||
return;
|
||||
}
|
||||
|
||||
drawBoard();
|
||||
const data = await response.json();
|
||||
|
||||
applyLoadedGame(data);
|
||||
setStatus(`Speicherstand #${savePointId} geladen.`, "green");
|
||||
toggleSavePointsPanel(false);
|
||||
}
|
||||
|
||||
pendingBoardSize = boardSize;
|
||||
updateModeButtons();
|
||||
updateBoardSizeDisplays(boardSize);
|
||||
drawBoard();
|
||||
|
||||
Reference in New Issue
Block a user