test
This commit is contained in:
28
app.py
28
app.py
@ -11,11 +11,11 @@ DB_PATH = str(BASE_DIR / "schiffe_versenken.db")
|
|||||||
BOARD_SIZE = 10
|
BOARD_SIZE = 10
|
||||||
|
|
||||||
FLEET = [
|
FLEET = [
|
||||||
{"id": "koenig", "name": "Oberidiot", "size": 5, "hat": "#c0392b"},
|
{"id": "koenig", "name": "Oberwappler", "size": 5, "hat": "#c0392b"},
|
||||||
{"id": "hauptmann", "name": "Chefidiot", "size": 4, "hat": "#2980b9"},
|
{"id": "hauptmann", "name": "Chefwappler", "size": 4, "hat": "#2980b9"},
|
||||||
{"id": "krieger1", "name": "Idiot I", "size": 3, "hat": "#27ae60"},
|
{"id": "krieger1", "name": "Wappler I", "size": 3, "hat": "#27ae60"},
|
||||||
{"id": "krieger2", "name": "Idiot II", "size": 3, "hat": "#8e44ad"},
|
{"id": "krieger2", "name": "Wappler II", "size": 3, "hat": "#8e44ad"},
|
||||||
{"id": "kundschafter", "name": "Trotteltrupp", "size": 2, "hat": "#d35400"},
|
{"id": "kundschafter", "name": "Wapplertrupp", "size": 2, "hat": "#d35400"},
|
||||||
]
|
]
|
||||||
FLEET_BY_ID = {s["id"]: s for s in FLEET}
|
FLEET_BY_ID = {s["id"]: s for s in FLEET}
|
||||||
|
|
||||||
@ -116,7 +116,7 @@ def new_game_row():
|
|||||||
"computer_shots": empty_shots(),
|
"computer_shots": empty_shots(),
|
||||||
"computer_ai_state": {"mode": "random", "targets": [], "tried": []},
|
"computer_ai_state": {"mode": "random", "targets": [], "tried": []},
|
||||||
"status": "placing",
|
"status": "placing",
|
||||||
"message": "Platziere deine Idioten auf dem Spielfeld.",
|
"message": "Platziere deine Wappler auf dem Spielfeld.",
|
||||||
}
|
}
|
||||||
db = get_db()
|
db = get_db()
|
||||||
db.execute(
|
db.execute(
|
||||||
@ -311,22 +311,22 @@ def api_place_ship():
|
|||||||
|
|
||||||
ship = FLEET_BY_ID.get(ship_id)
|
ship = FLEET_BY_ID.get(ship_id)
|
||||||
if ship is None:
|
if ship is None:
|
||||||
return jsonify({"error": "Unbekannter Idiot."}), 400
|
return jsonify({"error": "Unbekannter Wappler."}), 400
|
||||||
|
|
||||||
board = game["player_board"]
|
board = game["player_board"]
|
||||||
already_placed = any(ship_id in row_ for row_ in board)
|
already_placed = any(ship_id in row_ for row_ in board)
|
||||||
if already_placed:
|
if already_placed:
|
||||||
return jsonify({"error": "Dieser Idiot steht schon auf dem Feld."}), 400
|
return jsonify({"error": "Dieser Wappler steht schon auf dem Feld."}), 400
|
||||||
|
|
||||||
if not can_place(board, row, col, ship["size"], orientation):
|
if not can_place(board, row, col, ship["size"], orientation):
|
||||||
return jsonify({"error": "Hier passt der Idiot nicht hin."}), 400
|
return jsonify({"error": "Hier passt der Wappler nicht hin."}), 400
|
||||||
|
|
||||||
place_ship(board, ship_id, row, col, ship["size"], orientation)
|
place_ship(board, ship_id, row, col, ship["size"], orientation)
|
||||||
game["player_board"] = board
|
game["player_board"] = board
|
||||||
|
|
||||||
placed_ids = {cell for row_ in board for cell in row_ if cell is not None}
|
placed_ids = {cell for row_ in board for cell in row_ if cell is not None}
|
||||||
if len(placed_ids) == len(FLEET):
|
if len(placed_ids) == len(FLEET):
|
||||||
game["message"] = "Alle Idioten stehen bereit! Klicke auf 'Kampf beginnen'."
|
game["message"] = "Alle Wappler stehen bereit! Klicke auf 'Kampf beginnen'."
|
||||||
else:
|
else:
|
||||||
game["message"] = f"{ship['name']} platziert."
|
game["message"] = f"{ship['name']} platziert."
|
||||||
|
|
||||||
@ -340,7 +340,7 @@ def api_random_place():
|
|||||||
if game is None or game["status"] != "placing":
|
if game is None or game["status"] != "placing":
|
||||||
return jsonify({"error": "Kein aktives Platzierungsspiel."}), 400
|
return jsonify({"error": "Kein aktives Platzierungsspiel."}), 400
|
||||||
game["player_board"] = random_place_all_ships()
|
game["player_board"] = random_place_all_ships()
|
||||||
game["message"] = "Alle Idioten wurden zufällig aufgestellt."
|
game["message"] = "Alle Wappler wurden zufällig aufgestellt."
|
||||||
save_game(game)
|
save_game(game)
|
||||||
return jsonify(build_state_payload(game))
|
return jsonify(build_state_payload(game))
|
||||||
|
|
||||||
@ -364,7 +364,7 @@ def api_start():
|
|||||||
|
|
||||||
placed_ids = {cell for row_ in game["player_board"] for cell in row_ if cell is not None}
|
placed_ids = {cell for row_ in game["player_board"] for cell in row_ if cell is not None}
|
||||||
if len(placed_ids) != len(FLEET):
|
if len(placed_ids) != len(FLEET):
|
||||||
return jsonify({"error": "Bitte alle Idioten platzieren, bevor der Kampf beginnt."}), 400
|
return jsonify({"error": "Bitte alle Wappler platzieren, bevor der Kampf beginnt."}), 400
|
||||||
|
|
||||||
game["status"] = "playing"
|
game["status"] = "playing"
|
||||||
game["message"] = "Der Kampf hat begonnen! Du bist am Zug – ziehe in die feindlichen Berge."
|
game["message"] = "Der Kampf hat begonnen! Du bist am Zug – ziehe in die feindlichen Berge."
|
||||||
@ -401,12 +401,12 @@ def api_fire():
|
|||||||
computer_result = None
|
computer_result = None
|
||||||
if all_ships_sunk(game["computer_board"], game["player_shots"]):
|
if all_ships_sunk(game["computer_board"], game["player_shots"]):
|
||||||
game["status"] = "player_win"
|
game["status"] = "player_win"
|
||||||
game["message"] = "Sieg! Du hast alle Idioten des Computers versenkt."
|
game["message"] = "Sieg! Du hast alle Wappler des Computers versenkt."
|
||||||
else:
|
else:
|
||||||
computer_result = computer_take_shot(game)
|
computer_result = computer_take_shot(game)
|
||||||
if all_ships_sunk(game["player_board"], game["computer_shots"]):
|
if all_ships_sunk(game["player_board"], game["computer_shots"]):
|
||||||
game["status"] = "computer_win"
|
game["status"] = "computer_win"
|
||||||
game["message"] = "Niederlage! Der Computer hat all deine Idioten besiegt."
|
game["message"] = "Niederlage! Der Computer hat all deine Wappler besiegt."
|
||||||
else:
|
else:
|
||||||
msgs = []
|
msgs = []
|
||||||
msgs.append(
|
msgs.append(
|
||||||
|
|||||||
@ -148,34 +148,34 @@ button:disabled { opacity: 0.4; cursor: not-allowed; }
|
|||||||
font-size: 18px;
|
font-size: 18px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* --- Dwarf ship sprite, built purely from CSS shapes --- */
|
/* --- Wappler ship sprite, built purely from CSS shapes --- */
|
||||||
.dwarf {
|
.wappler {
|
||||||
position: relative;
|
position: relative;
|
||||||
width: 24px;
|
width: 24px;
|
||||||
height: 26px;
|
height: 26px;
|
||||||
}
|
}
|
||||||
.dwarf .hat {
|
.wappler .cap {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: -2px;
|
top: -1px;
|
||||||
left: 2px;
|
left: 2px;
|
||||||
width: 0;
|
width: 18px;
|
||||||
height: 0;
|
height: 9px;
|
||||||
border-left: 10px solid transparent;
|
background: var(--hat, #c0392b);
|
||||||
border-right: 10px solid transparent;
|
border-radius: 9px 9px 3px 3px;
|
||||||
border-bottom: 12px solid var(--hat, #c0392b);
|
transform: rotate(-8deg);
|
||||||
filter: drop-shadow(0 1px 0 rgba(0,0,0,0.4));
|
filter: drop-shadow(0 1px 0 rgba(0,0,0,0.4));
|
||||||
}
|
}
|
||||||
.dwarf .hat::after {
|
.wappler .cap::after {
|
||||||
content: "";
|
content: "";
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 11px;
|
top: 6px;
|
||||||
left: -6px;
|
right: -7px;
|
||||||
width: 12px;
|
width: 10px;
|
||||||
height: 4px;
|
height: 4px;
|
||||||
background: var(--hat, #c0392b);
|
background: var(--hat, #c0392b);
|
||||||
border-radius: 2px;
|
border-radius: 0 4px 4px 0;
|
||||||
}
|
}
|
||||||
.dwarf .head {
|
.wappler .head {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 9px;
|
top: 9px;
|
||||||
left: 4px;
|
left: 4px;
|
||||||
@ -184,28 +184,38 @@ button:disabled { opacity: 0.4; cursor: not-allowed; }
|
|||||||
background: #f0c090;
|
background: #f0c090;
|
||||||
border-radius: 50% 50% 45% 45%;
|
border-radius: 50% 50% 45% 45%;
|
||||||
}
|
}
|
||||||
.dwarf .beard {
|
.wappler .stoppel {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 15px;
|
top: 17px;
|
||||||
left: 3px;
|
left: 4px;
|
||||||
width: 18px;
|
width: 16px;
|
||||||
height: 12px;
|
height: 6px;
|
||||||
background: #d9d9d9;
|
background: rgba(60, 40, 20, 0.28);
|
||||||
border-radius: 0 0 9px 9px;
|
border-radius: 0 0 8px 8px;
|
||||||
}
|
}
|
||||||
.dwarf .eyes {
|
.wappler .brille {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 13px;
|
top: 13px;
|
||||||
left: 7px;
|
left: 5px;
|
||||||
width: 10px;
|
width: 14px;
|
||||||
height: 2px;
|
height: 4px;
|
||||||
background: transparent;
|
background: #111;
|
||||||
box-shadow: 0 0 0 1px #2a1c00, 8px 0 0 -1px #2a1c00;
|
border-radius: 2px;
|
||||||
|
box-shadow: 2px -1px 0 -1px rgba(255,255,255,0.6);
|
||||||
|
}
|
||||||
|
.wappler .grinser {
|
||||||
|
position: absolute;
|
||||||
|
top: 18px;
|
||||||
|
left: 8px;
|
||||||
|
width: 8px;
|
||||||
|
height: 4px;
|
||||||
|
border-bottom: 2px solid #7a4a2a;
|
||||||
|
border-radius: 0 0 8px 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.cell.hit .dwarf { filter: grayscale(1) brightness(0.6); transform: rotate(90deg); }
|
.cell.hit .wappler { filter: grayscale(1) brightness(0.6); transform: rotate(90deg); }
|
||||||
.cell.hit .dwarf .eyes { box-shadow: none; }
|
.cell.hit .wappler .brille { box-shadow: none; }
|
||||||
.cell.hit .dwarf::before {
|
.cell.hit .wappler::before {
|
||||||
content: "✕";
|
content: "✕";
|
||||||
position: absolute;
|
position: absolute;
|
||||||
inset: 0;
|
inset: 0;
|
||||||
|
|||||||
@ -29,11 +29,13 @@ function setMessage(text, isError = false) {
|
|||||||
m.style.color = isError ? "#ff8b8b" : "";
|
m.style.color = isError ? "#ff8b8b" : "";
|
||||||
}
|
}
|
||||||
|
|
||||||
function dwarfSprite(hat) {
|
function wapplerSprite(hat) {
|
||||||
const wrap = document.createElement("div");
|
const wrap = document.createElement("div");
|
||||||
wrap.className = "dwarf";
|
wrap.className = "wappler";
|
||||||
wrap.style.setProperty("--hat", hat);
|
wrap.style.setProperty("--hat", hat);
|
||||||
wrap.innerHTML = '<div class="hat"></div><div class="head"></div><div class="eyes"></div><div class="beard"></div>';
|
wrap.innerHTML =
|
||||||
|
'<div class="cap"></div><div class="head"></div><div class="brille"></div>' +
|
||||||
|
'<div class="grinser"></div><div class="stoppel"></div>';
|
||||||
return wrap;
|
return wrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -95,7 +97,7 @@ function renderPlacementBoard(data) {
|
|||||||
const shipId = data.player_board[r][c];
|
const shipId = data.player_board[r][c];
|
||||||
if (shipId) {
|
if (shipId) {
|
||||||
const ship = data.fleet_def.find((s) => s.id === shipId);
|
const ship = data.fleet_def.find((s) => s.id === shipId);
|
||||||
cell.appendChild(dwarfSprite(ship.hat));
|
cell.appendChild(wapplerSprite(ship.hat));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -105,7 +107,7 @@ function renderPlacementBoard(data) {
|
|||||||
|
|
||||||
async function handlePlacementClick(r, c) {
|
async function handlePlacementClick(r, c) {
|
||||||
if (!state.selectedShip) {
|
if (!state.selectedShip) {
|
||||||
setMessage("Wähle zuerst einen Idioten aus.", true);
|
setMessage("Wähle zuerst einen Wappler aus.", true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
@ -135,7 +137,7 @@ function renderBattle(data) {
|
|||||||
own.innerHTML = "";
|
own.innerHTML = "";
|
||||||
const shipId = data.player_board[r][c];
|
const shipId = data.player_board[r][c];
|
||||||
const receivedShot = data.player_shots_received[r][c];
|
const receivedShot = data.player_shots_received[r][c];
|
||||||
if (shipId) own.appendChild(dwarfSprite(data.fleet_def.find((s) => s.id === shipId).hat));
|
if (shipId) own.appendChild(wapplerSprite(data.fleet_def.find((s) => s.id === shipId).hat));
|
||||||
if (receivedShot) own.classList.add(receivedShot);
|
if (receivedShot) own.classList.add(receivedShot);
|
||||||
|
|
||||||
const enemy = enemyCells[r][c];
|
const enemy = enemyCells[r][c];
|
||||||
|
|||||||
@ -3,21 +3,21 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
<title>Idioten versenken</title>
|
<title>Wappler versenken</title>
|
||||||
<link rel="stylesheet" href="{{ url_for('static', filename='css/style.css') }}">
|
<link rel="stylesheet" href="{{ url_for('static', filename='css/style.css') }}">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div class="app">
|
<div class="app">
|
||||||
<header>
|
<header>
|
||||||
<h1>🤡 Idioten versenken</h1>
|
<h1>🕶️ Wappler versenken</h1>
|
||||||
<p class="subtitle">Schiffe versenken mit Idioten – du gegen den Computer</p>
|
<p class="subtitle">Schiffe versenken mit Wapplern – du gegen den Computer</p>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<div id="message" class="message"></div>
|
<div id="message" class="message"></div>
|
||||||
|
|
||||||
<section id="placement-screen" class="screen">
|
<section id="placement-screen" class="screen">
|
||||||
<h2>Aufstellung</h2>
|
<h2>Aufstellung</h2>
|
||||||
<p>Wähle einen Idioten und klicke auf dein Feld, um ihn zu platzieren.</p>
|
<p>Wähle einen Wappler und klicke auf dein Feld, um ihn zu platzieren.</p>
|
||||||
|
|
||||||
<div class="placement-controls">
|
<div class="placement-controls">
|
||||||
<div id="fleet-picker" class="fleet-picker"></div>
|
<div id="fleet-picker" class="fleet-picker"></div>
|
||||||
|
|||||||
Reference in New Issue
Block a user