Wertung 4: Sieg 2 / Unentschieden 1 — je SATZ (der STFV-Modus)

Der STFV wertet jeden gewonnenen Satz mit 2 Spielpunkten und jedes
Satz-Unentschieden mit 1 Punkt je Seite. Das ist ein neuer Wert der
bestehenden Stellschraube spielpunkte_wertung_* (tinyint, Wert 4), keine
neue Spalte und keine Kombinatorik (#306-Konzept, Abschnitt 1). Nach aussen
bleibt der Modus punktetyp 1 + satzergebnisse 1 — die Punkte eines Spiels
SIND weiter die gewonnenen Saetze, punktetyp behaelt seine zwei Werte.

Die Regel sitzt in spielpunkteFuerSpiel(): 2·gewonnene + 1·unentschiedene
Saetze je Seite aus ergebnis_detailliert; ohne Detail (App-Meldung nur mit
Satzstand) deterministisch 2·punkte — Satzsiege sind bekannt, Remis nicht.
Beide PHP-Rechenwege bekommen dadurch denselben neuen Fall geschenkt.

Das Formular bietet die Option nur an, wo sie rechenbar ist: sichtbar
allein bei "Mehrere - mit Ergebnis je Satz" (data-satzdetails-Option,
zeilen_anzeigen() blendet sie sonst aus und stellt die Auswahl zurueck).
Der Server verlaesst sich darauf nicht: kommt Wertung 4 ohne punktetyp 1 +
satzergebnisse 1 an, faellt sie auf 0 zurueck (Sieg 2/Remis 1 je Spiel, die
naechstliegende Je-Spiel-Regel) — die Spielform dominiert, wie im
Bestandscode. Race schliesst 4 automatisch aus, weil Race Wertung 2
erzwingt. Gemessen an drei ueber das echte Formular geposteten Modi:
Einer+4 -> 0, Mehrere-ohne-Satzerfassung+4 -> 0, Race+4 -> 2. Und der
Warnhinweis beim Speichern mit Bestandsergebnissen nennt jetzt auch den
STFV-Altbestandsfall: wo unter "Mehrere" in Wahrheit Tore in den
Punkte-Feldern stehen, zaehlt nach dem Wechsel jedes Tor wie ein gewonnener
Satz doppelt — umgerechnet wird nichts.

Die DRITTE Rechenstelle rechnet mit — die Lehre aus dem Remis-Vorfall
(Formular 7:3, Server 6:2): das erzeugte Formular-JavaScript bekommt EINE
gemeinsame Satzsummen-Hilfsfunktion (satz_summen liest die
_punkte_detailliert-Felder und ueberspringt 0:0-Saetze wie die
Satz-Zaehlschleife des Servers), und BEIDE Verbraucher benutzen sie:
gesamtstand_aktualisieren() und spielpunkte_bedingung(). Aus den
spiel_N_*_punkte-Feldern duerfen beide im 4er-Modus nicht mehr rechnen —
dort stehen Saetze, eine Je-Spiel-Wertung waere falsch.

End-to-end gemessen an der neuen Prueflige G (Modus ueber das echte
Formular angelegt: 3 Spiele E/E/D, 4 feste Saetze, Wertung je Satz;
punkte_sieg wird -3 gespiegelt):

  - Begegnung 117 mit Satz-Remis in allen drei Spielen ("6:4 4:4 2:6 6:3",
    "1:6 2:6 6:6 0:6", "4:4 4:4 6:2 2:6"): gespeichert 5:3, 1:7, 4:4 —
    Begegnung 10:14 Spielpunkte, 3:5 Saetze, 43:57 Tore. Handrechnung
    identisch.
  - JS-Paritaet nicht nur gelesen, sondern gerechnet: das vom PHP erzeugte
    JavaScript aus der gerenderten Seite in node gegen dieselben Feldwerte
    laufen lassen — gesamtstand_aktualisieren liefert 10:14,
    spielpunkte_bedingung summiert 10/14, satz_summen je Spiel
    [2,1,1] [0,1,3] [1,2,1]; die Datenbank derselben Begegnung sagt 10:14.
  - Fallback gemessen (Begegnung 119, Meldung wie ein Altclient nur mit
    Satzstand 2:1 und 1:1): Spielpunkte 4:2 und 2:2 (= 2·punkte),
    ergebnis_detailliert bleibt leer, Tore bleiben NULL.
  - Bestandsmodi unveraendert: dieselbe Messlatte wie bei den beiden
    Schritten zuvor (10 Modi + 10 Begegnungen unveraendert gespeichert,
    756 Zeilen Datenbank-Dump, 30995 Zeilen Frontend-HTML): VOLLSTAENDIG
    identisch.

Neue Sprachschluessel DE/EN: die Option ("Sieg: 2 Punkte, Unentschieden:
1 Punkt - je Satz") und der erweiterte Warnhinweis.
This commit is contained in:
svennickel
2026-09-09 19:09:12 +02:00
parent 79f3abc01a
commit 64588f71ac
6 changed files with 84 additions and 18 deletions
@@ -4724,6 +4724,10 @@ function adminEditTeamspielmodus(): void
$spielpunkte_wertung_einzel = $jInput->get('spielpunkte_wertung_einzel', 0, 'INT');
$spielpunkte_wertung_doppel_separat = $jInput->get('spielpunkte_wertung_doppel_separat', 0, 'INT');
$spielpunkte_wertung_doppel = $spielpunkte_wertung_doppel_separat ? $jInput->get('spielpunkte_wertung_doppel', 0, 'INT') : $spielpunkte_wertung_einzel;
if ($spielpunkte_wertung_einzel == 4 && ($punktetyp != 1 || $satzergebnisse != 1))
$spielpunkte_wertung_einzel = 0;
if ($spielpunkte_wertung_doppel == 4 && ($punktetyp != 1 || $satzergebnisse != 1))
$spielpunkte_wertung_doppel = 0;
$spielpunkte_bedingung = $jInput->get('spielpunkte_bedingung', 0, 'INT');
$spielernamen = $jInput->get('spielernamen', 0, 'INT');
@@ -11264,19 +11268,20 @@ function adminSaveBegegnungSpielplan(): void
$total_heim_punkte += $spiel_heim_punkte;
$total_gast_punkte += $spiel_gast_punkte;
$spiel_ergebnis_detailliert = $skip_spiel
? (isset($spiele_nummern[$spiel_nr]) ? $spiele_nummern[$spiel_nr]->ergebnis_detailliert : null)
: $ergebnis_detailliert;
list($spiel_heim_spielpunkte, $spiel_gast_spielpunkte) = spielpunkteFuerSpiel(
$spieltyp[0] != "D" ? $spielpunkte_wertung_einzel : $spielpunkte_wertung_doppel,
$spiel_heim_punkte, $spiel_gast_punkte);
$spiel_heim_punkte, $spiel_gast_punkte, $spiel_ergebnis_detailliert);
$total_heim_spielpunkte += $spiel_heim_spielpunkte;
$total_gast_spielpunkte += $spiel_gast_spielpunkte;
list($spiel_heim_tore, $spiel_gast_tore) = toreFuerSpiel(
$teamspiel_modus->punktetyp, $teamspiel_modus->satzergebnisse ?? 0,
$spiel_heim_punkte, $spiel_gast_punkte,
$skip_spiel
? (isset($spiele_nummern[$spiel_nr]) ? $spiele_nummern[$spiel_nr]->ergebnis_detailliert : null)
: $ergebnis_detailliert);
$spiel_heim_punkte, $spiel_gast_punkte, $spiel_ergebnis_detailliert);
if ($spiel_heim_tore === null || $total_heim_tore === null) {
$total_heim_tore = null;
$total_gast_tore = null;
@@ -6180,7 +6180,7 @@ function begegnungenAktualisieren($geaendert_veranstaltung_id = 0, $geaendert_mo
list($spiel_heim_spielpunkte, $spiel_gast_spielpunkte) = spielpunkteFuerSpiel(
$spiel->heim_spieler_2_id === NULL && $spiel->gast_spieler_2_id === NULL
? $spielpunkte_wertung_einzel : $spielpunkte_wertung_doppel,
$spiel_heim_punkte, $spiel_gast_punkte);
$spiel_heim_punkte, $spiel_gast_punkte, $spiel->ergebnis_detailliert);
$total_heim_spielpunkte += $spiel_heim_spielpunkte;
$total_gast_spielpunkte += $spiel_gast_spielpunkte;
@@ -628,10 +628,16 @@ function runden_detailliert_auswertung($runden): array
return array($ergebnis, $heim_saetze, $unentschieden_saetze, $gast_saetze, $heim_punkte, $gast_punkte);
}
function spielpunkteFuerSpiel($wertung, $heim_punkte, $gast_punkte): array
function spielpunkteFuerSpiel($wertung, $heim_punkte, $gast_punkte, $ergebnis_detailliert = null): array
{
if ($wertung == 2)
return array($heim_punkte, $gast_punkte);
if ($wertung == 4) {
if (empty($ergebnis_detailliert))
return array(2 * (int)$heim_punkte, 2 * (int)$gast_punkte);
$auswertung = runden_detailliert_auswertung($ergebnis_detailliert);
return array(2 * $auswertung[1] + $auswertung[2], 2 * $auswertung[3] + $auswertung[2]);
}
$sieg = $wertung == 0 ? 2 : ($wertung == 1 ? 3 : 1);
if ($heim_punkte > $gast_punkte)
return array($sieg, 0);
@@ -4722,6 +4722,18 @@ class HTML_sportsmanager_admin
if (text != null) sel.options[o].textContent = text;
}
}
var satzdetails = saetze && document.adminForm.punktetyp.value == 2;
var wertungen = ['spielpunkte_wertung_einzel', 'spielpunkte_wertung_doppel'];
for (var f = 0; f < wertungen.length; f++) {
var sel = document.adminForm[wertungen[f]];
if (sel == null) continue;
for (var o = 0; o < sel.options.length; o++) {
if (sel.options[o].getAttribute('data-satzdetails') == null) continue;
sel.options[o].hidden = !satzdetails;
sel.options[o].disabled = !satzdetails;
if (!satzdetails && sel.value == sel.options[o].value) sel.value = '0';
}
}
if (race) {
var spiele = 0;
for (var n = 1; ; n++) {
@@ -4846,11 +4858,19 @@ class HTML_sportsmanager_admin
var _punktetyp_vorher = <?php echo !empty($row->satzergebnisse) ? 2 : (int)($row->punktetyp ?? 0); ?>;
var _hat_ergebnisse = <?php echo !empty($row->hat_ergebnisse) ? 'true' : 'false'; ?>;
var _wertung4_vorher = <?php echo !empty($row) && ($row->spielpunkte_wertung_einzel == 4 || $row->spielpunkte_wertung_doppel == 4) ? 'true' : 'false'; ?>;
function punktetyp_geprueft() {
if (!_hat_ergebnisse) return true;
if (Number(document.adminForm.punktetyp.value) === _punktetyp_vorher) return true;
return confirm(<?php echo json_encode(Text::_('COM_SPORTSMANAGER_POINT_TYPE_CHANGED_WARNING')); ?>);
if (Number(document.adminForm.punktetyp.value) !== _punktetyp_vorher
&& !confirm(<?php echo json_encode(Text::_('COM_SPORTSMANAGER_POINT_TYPE_CHANGED_WARNING')); ?>))
return false;
var separat = document.adminForm.spielpunkte_wertung_doppel_separat.value == 1;
var wertung4 = document.adminForm.spielpunkte_wertung_einzel.value == 4
|| (separat && document.adminForm.spielpunkte_wertung_doppel.value == 4);
if (wertung4 !== _wertung4_vorher)
return confirm(<?php echo json_encode(Text::_('COM_SPORTSMANAGER_PER_SET_VALUATION_CHANGED_WARNING')); ?>);
return true;
}
function spielpunkte_wertung_doppel_anzeigen() {
@@ -5100,12 +5120,14 @@ class HTML_sportsmanager_admin
name="spielpunkte_wertung_einzel" id="points_valuation_single"
size="1">
<?php
$typ = array(Text::_('COM_SPORTSMANAGER_WIN_1_POINT'), Text::_('COM_SPORTSMANAGER_WIN_2_POINTS'), Text::_('COM_SPORTSMANAGER_WIN_3_POINTS'), Text::_('COM_SPORTSMANAGER_RESULT_AS_GAME_POINTS'));
$typ_wert = array(3, 0, 1, 2);
$typ = array(Text::_('COM_SPORTSMANAGER_WIN_1_POINT'), Text::_('COM_SPORTSMANAGER_WIN_2_POINTS'), Text::_('COM_SPORTSMANAGER_WIN_3_POINTS'), Text::_('COM_SPORTSMANAGER_RESULT_AS_GAME_POINTS'), Text::_('COM_SPORTSMANAGER_WIN_2_POINTS_PER_SET'));
$typ_wert = array(3, 0, 1, 2, 4);
$einheit_attr = ' data-tore="' . htmlspecialchars(Text::_('COM_SPORTSMANAGER_RESULT_AS_GAME_POINTS_GOALS'), ENT_QUOTES) . '"'
. ' data-saetze="' . htmlspecialchars(Text::_('COM_SPORTSMANAGER_RESULT_AS_GAME_POINTS_SETS'), ENT_QUOTES) . '"';
for ($i = 0; $i <= 3; $i++) {
echo "<option value=\"" . $typ_wert[$i] . "\"" . ($typ_wert[$i] == 2 ? $einheit_attr : "") . ($row != null ? ($row->spielpunkte_wertung_einzel == $typ_wert[$i] ? " selected" : "") : "") . ">" . htmlentities_utf8($typ[$i]) . "</option>";
$satzdetails_an = !$race_an && !empty($row) && !empty($row->satzergebnisse) && $row->punktetyp != 0;
$satzdetails_attr = ' data-satzdetails="1"' . ($satzdetails_an ? '' : ' hidden disabled');
for ($i = 0; $i <= 4; $i++) {
echo "<option value=\"" . $typ_wert[$i] . "\"" . ($typ_wert[$i] == 2 ? $einheit_attr : "") . ($typ_wert[$i] == 4 ? $satzdetails_attr : "") . ($row != null ? ($row->spielpunkte_wertung_einzel == $typ_wert[$i] ? " selected" : "") : "") . ">" . htmlentities_utf8($typ[$i]) . "</option>";
}
?>
</select>
@@ -5126,9 +5148,9 @@ class HTML_sportsmanager_admin
name="spielpunkte_wertung_doppel" id="points_valuation_double"
size="1">
<?php
$typ = array(Text::_('COM_SPORTSMANAGER_WIN_1_POINT'), Text::_('COM_SPORTSMANAGER_WIN_2_POINTS'), Text::_('COM_SPORTSMANAGER_WIN_3_POINTS'), Text::_('COM_SPORTSMANAGER_RESULT_AS_GAME_POINTS'));
for ($i = 0; $i <= 3; $i++) {
echo "<option value=\"" . $typ_wert[$i] . "\"" . ($typ_wert[$i] == 2 ? $einheit_attr : "") . ($row != null ? ($row->spielpunkte_wertung_doppel == $typ_wert[$i] ? " selected" : "") : "") . ">" . htmlentities_utf8($typ[$i]) . "</option>";
$typ = array(Text::_('COM_SPORTSMANAGER_WIN_1_POINT'), Text::_('COM_SPORTSMANAGER_WIN_2_POINTS'), Text::_('COM_SPORTSMANAGER_WIN_3_POINTS'), Text::_('COM_SPORTSMANAGER_RESULT_AS_GAME_POINTS'), Text::_('COM_SPORTSMANAGER_WIN_2_POINTS_PER_SET'));
for ($i = 0; $i <= 4; $i++) {
echo "<option value=\"" . $typ_wert[$i] . "\"" . ($typ_wert[$i] == 2 ? $einheit_attr : "") . ($typ_wert[$i] == 4 ? $satzdetails_attr : "") . ($row != null ? ($row->spielpunkte_wertung_doppel == $typ_wert[$i] ? " selected" : "") : "") . ">" . htmlentities_utf8($typ[$i]) . "</option>";
}
?>
</select>
@@ -12721,11 +12743,33 @@ static function adminVerbandsorganMitglieder($rows,$verbandsorgan): void
. "}\n";
}
}
if ($spielpunkte_wertung_einzel == 4 || $spielpunkte_wertung_doppel == 4) {
echo "function satz_summen(nr) {\n"
. " var heim = document.getElementsByName('spiel_' + nr + '_heim_punkte_detailliert[]');\n"
. " var gast = document.getElementsByName('spiel_' + nr + '_gast_punkte_detailliert[]');\n"
. " var gewonnen = 0, unentschieden = 0, verloren = 0;\n"
. " for (var i = 0; i < heim.length; i++) {\n"
. " var h = heim[i].value == '' ? 0 : Number(heim[i].value);\n"
. " var g = gast[i].value == '' ? 0 : Number(gast[i].value);\n"
. " if (h == 0 && g == 0) continue;\n"
. " if (h > g) gewonnen++;\n"
. " else if (g > h) verloren++;\n"
. " else unentschieden++;\n"
. " }\n"
. " return [gewonnen, unentschieden, verloren];\n"
. "}\n";
}
echo "function gesamtstand_aktualisieren() {\n"
. " var sh = 0, sg = 0, h, g;\n";
. " var sh = 0, sg = 0, h, g, satzstand;\n";
for ($i = 1; $i <= count($spieltypen); $i++) {
$d = $spieltypen[$i - 1][0] == "D";
$w = $d ? $spielpunkte_wertung_doppel : $spielpunkte_wertung_einzel;
if ($w == 4) {
echo " satzstand = satz_summen(" . $i . ");\n"
. " sh += 2 * satzstand[0] + satzstand[1];\n"
. " sg += 2 * satzstand[2] + satzstand[1];\n";
continue;
}
echo " h = document.adminForm['spiel_" . $i . "_heim_punkte'];\n"
. " g = document.adminForm['spiel_" . $i . "_gast_punkte'];\n"
. " h = (h != null && h.value != '') ? Number(h.value) : 0;\n"
@@ -12879,12 +12923,19 @@ static function adminVerbandsorganMitglieder($rows,$verbandsorgan): void
. " var spielpunkte_unentschieden_einzel = " . ($spielpunkte_wertung_einzel <= 1 ? 1 : 0) . ";\n"
. " var spielpunkte_unentschieden_doppel = " . ($spielpunkte_wertung_doppel <= 1 ? 1 : 0) . ";\n"
. " var punkte_heim;\n"
. " var punkte_gast;\n";
. " var punkte_gast;\n"
. " var satzstand;\n";
$n = count($spieltypen);
for ($i = 1; $i <= $n; $i++) {
$spiel_einzel = $spieltypen[$i - 1][0] != "D";
if (($spiel_einzel && $spielpunkte_wertung_einzel == 2) || (!$spiel_einzel && $spielpunkte_wertung_doppel == 2))
continue;
if (($spiel_einzel ? $spielpunkte_wertung_einzel : $spielpunkte_wertung_doppel) == 4) {
echo " satzstand = satz_summen(" . $i . ");\n"
. " spielpunkte_heim += 2 * satzstand[0] + satzstand[1];\n"
. " spielpunkte_gast += 2 * satzstand[2] + satzstand[1];\n";
continue;
}
// Number(): .value ist eine Zeichenkette, "10" > "9" wäre sonst unwahr
echo " punkte_heim = document.adminForm.spiel_" . $i . "_heim_punkte.value != '' ? Number(document.adminForm.spiel_" . $i . "_heim_punkte.value) : 0;\n"
. " punkte_gast = document.adminForm.spiel_" . $i . "_gast_punkte.value != '' ? Number(document.adminForm.spiel_" . $i . "_gast_punkte.value) : 0;\n"
@@ -501,6 +501,7 @@ COM_SPORTSMANAGER_ADD_SEASON="Saison hinzuf&uuml;gen"
COM_SPORTSMANAGER_GAME_POINTS_GAME="Spielpunkte pro Spiel"
COM_SPORTSMANAGER_WIN_1_POINT="Sieg: 1 Punkt"
COM_SPORTSMANAGER_WIN_2_POINTS="Sieg: 2 Punkte, Unentschieden: 1 Punkt"
COM_SPORTSMANAGER_WIN_2_POINTS_PER_SET="Sieg: 2 Punkte, Unentschieden: 1 Punkt je Satz"
COM_SPORTSMANAGER_WIN_3_POINTS="Sieg: 3 Punkte, Unentschieden: 1 Punkt"
COM_SPORTSMANAGER_MEETING_CONCLUDED_AT="Begegnung abgeschlossen bei"
COM_SPORTSMANAGER_GAMES_IN_STATISTIK="Spiele in Spielerstatistik"
@@ -1174,5 +1175,6 @@ COM_SPORTSMANAGER_GROUP_SEQUENCE="Spielfolge"
COM_SPORTSMANAGER_GAMES_IN_STATISTIK_FIRST_ONE="nur das erste Spiel"
COM_SPORTSMANAGER_GAMES_IN_STATISTIK_FIRST="nur die ersten %d Spiele"
COM_SPORTSMANAGER_POINT_TYPE_CHANGED_WARNING="Für diesen Mannschaftsspielplan sind bereits Ergebnisse eingetragen. Die gespeicherten Zahlen werden NICHT umgerechnet sie bedeuten danach etwas anderes (Tore statt Sätze bzw. umgekehrt). Trotzdem speichern?"
COM_SPORTSMANAGER_PER_SET_VALUATION_CHANGED_WARNING="Für diesen Mannschaftsspielplan sind bereits Ergebnisse eingetragen. Beim Wechsel der Wertung „je Satz" wird nichts umgerechnet: Spiele ohne gespeicherte Satzergebnisse werden mit 2 Punkten je gewonnenem Satz gewertet, ihre Satz-Unentschieden gehen verloren. Und wo unter „Mehrere" in Wahrheit Tore in den Punkte-Feldern stehen (statt Sätzen), zählt ab jetzt jedes Tor wie ein gewonnener Satz doppelt. Trotzdem speichern?"
COM_SPORTSMANAGER_RACE_RUNNING="Stand"
COM_SPORTSMANAGER_TOTAL_SCORE="Gesamtstand"
@@ -501,6 +501,7 @@ COM_SPORTSMANAGER_ADD_SEASON="Add season"
COM_SPORTSMANAGER_GAME_POINTS_GAME="Game points per game"
COM_SPORTSMANAGER_WIN_1_POINT="Win: 1 point"
COM_SPORTSMANAGER_WIN_2_POINTS="Win: 2 points, draw: 1 point"
COM_SPORTSMANAGER_WIN_2_POINTS_PER_SET="Win: 2 points, draw: 1 point per set"
COM_SPORTSMANAGER_WIN_3_POINTS="Win: 3 points, draw: 1 point"
COM_SPORTSMANAGER_MEETING_CONCLUDED_AT="Match won at"
COM_SPORTSMANAGER_GAMES_IN_STATISTIK="Games in player statistics"
@@ -1174,5 +1175,6 @@ COM_SPORTSMANAGER_GROUP_SEQUENCE="Game sequence"
COM_SPORTSMANAGER_GAMES_IN_STATISTIK_FIRST_ONE="the first game only"
COM_SPORTSMANAGER_GAMES_IN_STATISTIK_FIRST="the first %d games only"
COM_SPORTSMANAGER_POINT_TYPE_CHANGED_WARNING="Results have already been entered for this team schedule. The stored numbers are NOT converted afterwards they mean something else (goals instead of sets, or the other way round). Save anyway?"
COM_SPORTSMANAGER_PER_SET_VALUATION_CHANGED_WARNING="Results have already been entered for this team schedule. Switching the per-set valuation converts nothing: games without stored set results are valued at 2 points per set won, and their drawn sets are lost. And where a 'multiple sets' schedule in fact holds goals in its points fields (instead of sets), every goal will from now on count double, like a set won. Save anyway?"
COM_SPORTSMANAGER_RACE_RUNNING="Score"
COM_SPORTSMANAGER_TOTAL_SCORE="Total score"