@jjlmoya/utils-civic 1.2.0 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/category/index.ts +2 -1
- package/src/entries.ts +2 -0
- package/src/tests/locale_completeness.test.ts +1 -1
- package/src/tests/tool_validation.test.ts +1 -1
- package/src/tool/coalition-majority-calculator/bibliography.astro +6 -0
- package/src/tool/coalition-majority-calculator/bibliography.ts +12 -0
- package/src/tool/coalition-majority-calculator/coalition-majority-calculator.css +467 -0
- package/src/tool/coalition-majority-calculator/component.astro +62 -0
- package/src/tool/coalition-majority-calculator/contract.test.ts +14 -0
- package/src/tool/coalition-majority-calculator/controller.ts +89 -0
- package/src/tool/coalition-majority-calculator/dom-views.ts +145 -0
- package/src/tool/coalition-majority-calculator/entry.ts +28 -0
- package/src/tool/coalition-majority-calculator/evaluator.ts +13 -0
- package/src/tool/coalition-majority-calculator/i18n/de.ts +51 -0
- package/src/tool/coalition-majority-calculator/i18n/en.ts +74 -0
- package/src/tool/coalition-majority-calculator/i18n/es.ts +51 -0
- package/src/tool/coalition-majority-calculator/i18n/fr.ts +12 -0
- package/src/tool/coalition-majority-calculator/i18n/id.ts +45 -0
- package/src/tool/coalition-majority-calculator/i18n/it.ts +12 -0
- package/src/tool/coalition-majority-calculator/i18n/ja.ts +21 -0
- package/src/tool/coalition-majority-calculator/i18n/ko.ts +21 -0
- package/src/tool/coalition-majority-calculator/i18n/nl.ts +12 -0
- package/src/tool/coalition-majority-calculator/i18n/pl.ts +12 -0
- package/src/tool/coalition-majority-calculator/i18n/pt.ts +12 -0
- package/src/tool/coalition-majority-calculator/i18n/ru.ts +12 -0
- package/src/tool/coalition-majority-calculator/i18n/sv.ts +12 -0
- package/src/tool/coalition-majority-calculator/i18n/tr.ts +12 -0
- package/src/tool/coalition-majority-calculator/i18n/zh.ts +12 -0
- package/src/tool/coalition-majority-calculator/index.ts +11 -0
- package/src/tool/coalition-majority-calculator/logic.test.ts +60 -0
- package/src/tool/coalition-majority-calculator/logic.ts +146 -0
- package/src/tool/coalition-majority-calculator/seo.astro +12 -0
- package/src/tool/coalition-majority-calculator/storage.ts +32 -0
- package/src/tool/coalition-majority-calculator/ui.ts +80 -0
- package/src/tools.ts +2 -0
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { analyzeCoalitions, type PartyInput } from './logic';
|
|
2
|
+
import { renderCoalitionResults, renderCoalitionScene, type CoalitionFilter } from './dom-views';
|
|
3
|
+
import { evaluateCoalitions } from './evaluator';
|
|
4
|
+
import { clearCoalitionState, loadCoalitionState, saveCoalitionState } from './storage';
|
|
5
|
+
import type { CoalitionMajorityUI } from './ui';
|
|
6
|
+
|
|
7
|
+
interface CoalitionConfig {
|
|
8
|
+
ui: CoalitionMajorityUI;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function readNumber(input: HTMLInputElement | null): number {
|
|
12
|
+
return input ? Number(input.value) : Number.NaN;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function partyInputs(root: HTMLElement): PartyInput[] {
|
|
16
|
+
return Array.from(root.querySelectorAll<HTMLElement>('[data-party-row]')).map((row) => ({
|
|
17
|
+
name: row.querySelector<HTMLInputElement>('[data-party-name]')?.value ?? '',
|
|
18
|
+
seats: readNumber(row.querySelector<HTMLInputElement>('[data-party-seats]')),
|
|
19
|
+
}));
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function renderStatus(root: HTMLElement, label: string, tone: string): void {
|
|
23
|
+
const status = root.querySelector<HTMLElement>('[data-coalition-status]');
|
|
24
|
+
if (!status) return;
|
|
25
|
+
status.textContent = label;
|
|
26
|
+
status.dataset.tone = tone;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function createPartyRow(ui: CoalitionMajorityUI, index: number, party: PartyInput): HTMLElement {
|
|
30
|
+
const row = document.createElement('div');
|
|
31
|
+
row.className = 'party-row';
|
|
32
|
+
row.dataset.partyRow = 'true';
|
|
33
|
+
row.innerHTML = `<label><span>${ui.partyName} ${index + 1}</span><input data-party-name type="text"></label><label><span>${ui.partySeats}</span><input data-party-seats type="number" min="0" step="1"></label><button type="button" class="icon-button" data-remove-party aria-label="${ui.removeParty} ${index + 1}">x</button>`;
|
|
34
|
+
const name = row.querySelector<HTMLInputElement>('[data-party-name]');
|
|
35
|
+
const seats = row.querySelector<HTMLInputElement>('[data-party-seats]');
|
|
36
|
+
if (name) name.value = party.name;
|
|
37
|
+
if (seats) seats.value = String(party.seats);
|
|
38
|
+
return row;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function calculate(root: HTMLElement, ui: CoalitionMajorityUI): void {
|
|
42
|
+
const threshold = readNumber(root.querySelector<HTMLInputElement>('[data-threshold]'));
|
|
43
|
+
const analysis = analyzeCoalitions(partyInputs(root), threshold);
|
|
44
|
+
const state = evaluateCoalitions(analysis);
|
|
45
|
+
const filter = root.querySelector<HTMLSelectElement>('[data-coalition-filter]')?.value as CoalitionFilter | undefined;
|
|
46
|
+
renderStatus(root, state.label, state.tone);
|
|
47
|
+
renderCoalitionScene(root, analysis, ui);
|
|
48
|
+
renderCoalitionResults(root, analysis, ui, filter ?? 'all');
|
|
49
|
+
saveCoalitionState({ parties: analysis.parties, threshold });
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function bindInteractions(root: HTMLElement, ui: CoalitionMajorityUI): void {
|
|
53
|
+
root.addEventListener('click', (event) => {
|
|
54
|
+
const target = event.target as HTMLElement;
|
|
55
|
+
if (target.closest('[data-add-party]')) {
|
|
56
|
+
const rows = root.querySelector<HTMLElement>('[data-parties]');
|
|
57
|
+
const count = root.querySelectorAll('[data-party-row]').length;
|
|
58
|
+
if (rows && count < 20) rows.append(createPartyRow(ui, count, { name: '', seats: 0 }));
|
|
59
|
+
calculate(root, ui);
|
|
60
|
+
}
|
|
61
|
+
if (target.closest('[data-remove-party]')) {
|
|
62
|
+
target.closest('[data-party-row]')?.remove();
|
|
63
|
+
calculate(root, ui);
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
root.addEventListener('input', () => calculate(root, ui));
|
|
67
|
+
root.querySelector('[data-coalition-filter]')?.addEventListener('change', () => calculate(root, ui));
|
|
68
|
+
root.querySelector('[data-reset]')?.addEventListener('click', () => {
|
|
69
|
+
clearCoalitionState();
|
|
70
|
+
window.location.reload();
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function restore(root: HTMLElement, ui: CoalitionMajorityUI): void {
|
|
75
|
+
const saved = loadCoalitionState();
|
|
76
|
+
if (!saved) return;
|
|
77
|
+
const rows = root.querySelector<HTMLElement>('[data-parties]');
|
|
78
|
+
if (!rows) return;
|
|
79
|
+
rows.innerHTML = '';
|
|
80
|
+
saved.parties.forEach((party, index) => rows.append(createPartyRow(ui, index, party)));
|
|
81
|
+
const threshold = root.querySelector<HTMLInputElement>('[data-threshold]');
|
|
82
|
+
if (threshold) threshold.value = String(saved.threshold);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export function mountCoalitionTool(root: HTMLElement, config: CoalitionConfig): void {
|
|
86
|
+
restore(root, config.ui);
|
|
87
|
+
bindInteractions(root, config.ui);
|
|
88
|
+
calculate(root, config.ui);
|
|
89
|
+
}
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import type { CoalitionAnalysis, CoalitionRecord, PartyInput } from './logic';
|
|
2
|
+
import type { CoalitionMajorityUI } from './ui';
|
|
3
|
+
|
|
4
|
+
interface ResultTargets {
|
|
5
|
+
status: HTMLElement;
|
|
6
|
+
stats: HTMLElement;
|
|
7
|
+
pivots: HTMLElement;
|
|
8
|
+
table: HTMLTableSectionElement;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export type CoalitionFilter = 'all' | 'minimal' | 'surplus';
|
|
12
|
+
|
|
13
|
+
interface PartyNodeLayout {
|
|
14
|
+
x: number;
|
|
15
|
+
y: number;
|
|
16
|
+
size: number;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function escapeHtml(value: string): string {
|
|
20
|
+
return value.replace(/[&<>"']/g, (character) => ({ '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' })[character] ?? character);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function number(value: number): string {
|
|
24
|
+
return new Intl.NumberFormat('en-US', { maximumFractionDigits: 0 }).format(value);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function percent(value: number): string {
|
|
28
|
+
return new Intl.NumberFormat('en-US', { style: 'percent', maximumFractionDigits: 1 }).format(value);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function partyNodeLayout(parties: PartyInput[]): PartyNodeLayout[] {
|
|
32
|
+
const centerX = 180;
|
|
33
|
+
const centerY = 150;
|
|
34
|
+
const radius = 104;
|
|
35
|
+
const maxSeats = Math.max(...parties.map((party) => party.seats), 1);
|
|
36
|
+
return parties.map((party, index) => {
|
|
37
|
+
const angle = -Math.PI / 2 + (index * Math.PI * 2) / parties.length;
|
|
38
|
+
const x = centerX + Math.cos(angle) * radius;
|
|
39
|
+
const y = centerY + Math.sin(angle) * radius;
|
|
40
|
+
const size = 11 + Math.sqrt(party.seats / maxSeats) * 25;
|
|
41
|
+
return { x, y, size };
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function coalitionLinks(parties: PartyInput[], analysis: CoalitionAnalysis, layouts: PartyNodeLayout[]): string {
|
|
46
|
+
const edges = new Set<string>();
|
|
47
|
+
analysis.displayedCoalitions.filter((coalition) => coalition.minimal).forEach((coalition) => {
|
|
48
|
+
for (let left = 0; left < parties.length; left += 1) {
|
|
49
|
+
if ((coalition.mask & (1 << left)) === 0) continue;
|
|
50
|
+
for (let right = left + 1; right < parties.length; right += 1) {
|
|
51
|
+
if ((coalition.mask & (1 << right)) === 0) continue;
|
|
52
|
+
edges.add(`${left}-${right}`);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
return [...edges].map((edge) => {
|
|
57
|
+
const indexes = edge.split('-').map(Number);
|
|
58
|
+
const left = indexes[0];
|
|
59
|
+
const right = indexes[1];
|
|
60
|
+
if (left === undefined || right === undefined) return '';
|
|
61
|
+
const start = layouts[left];
|
|
62
|
+
const end = layouts[right];
|
|
63
|
+
if (!start || !end) return '';
|
|
64
|
+
return `<line class="coalition-winning-link" x1="${start.x.toFixed(1)}" y1="${start.y.toFixed(1)}" x2="${end.x.toFixed(1)}" y2="${end.y.toFixed(1)}"/>`;
|
|
65
|
+
}).join('');
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function partyNodes(parties: PartyInput[], analysis: CoalitionAnalysis, layouts: PartyNodeLayout[]): string {
|
|
69
|
+
const centerX = 180;
|
|
70
|
+
const centerY = 150;
|
|
71
|
+
return parties.map((party, index) => {
|
|
72
|
+
const layout = layouts[index];
|
|
73
|
+
if (!layout) return '';
|
|
74
|
+
const pivot = analysis.pivotCounts[party.name] ?? 0;
|
|
75
|
+
return `<g class="coalition-node ${pivot > 0 ? 'is-pivotal' : ''}" transform="translate(${layout.x.toFixed(1)} ${layout.y.toFixed(1)})"><line class="coalition-link" x1="${(centerX - layout.x).toFixed(1)}" y1="${(centerY - layout.y).toFixed(1)}" x2="0" y2="0"/><circle r="${layout.size.toFixed(1)}"/><text class="coalition-node-name" y="-3">${escapeHtml(party.name)}</text><text class="coalition-node-seats" y="12">${number(party.seats)} seats</text></g>`;
|
|
76
|
+
}).join('');
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export function renderCoalitionScene(root: HTMLElement, analysis: CoalitionAnalysis, ui: CoalitionMajorityUI): void {
|
|
80
|
+
const scene = root.querySelector<HTMLElement>('[data-coalition-scene]');
|
|
81
|
+
if (!scene) return;
|
|
82
|
+
if (!analysis.valid || analysis.parties.length === 0) {
|
|
83
|
+
scene.innerHTML = `<div class="scene-empty">${escapeHtml(ui.emptyState)}</div>`;
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
const layouts = partyNodeLayout(analysis.parties);
|
|
87
|
+
scene.innerHTML = `<svg viewBox="0 0 360 300" role="img" aria-label="${escapeHtml(ui.coalitionSceneLabel)}"><circle class="coalition-orbit" cx="180" cy="150" r="58"/><g class="coalition-winning-links" aria-hidden="true">${coalitionLinks(analysis.parties, analysis, layouts)}</g><circle class="coalition-core" cx="180" cy="150" r="42"/><text class="coalition-core-label" x="180" y="146" text-anchor="middle">${number(analysis.threshold)}</text><text class="coalition-core-caption" x="180" y="163" text-anchor="middle">${escapeHtml(ui.threshold.toLowerCase())}</text>${partyNodes(analysis.parties, analysis, layouts)}</svg>`;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function stat(label: string, value: string): string {
|
|
91
|
+
return `<div class="coalition-stat"><dt>${escapeHtml(label)}</dt><dd>${escapeHtml(value)}</dd></div>`;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function coalitionRow(row: CoalitionRecord, ui: CoalitionMajorityUI): string {
|
|
95
|
+
const members = row.members.map(escapeHtml).join(', ');
|
|
96
|
+
const redundant = row.redundantMembers.length === 0 ? ui.none : row.redundantMembers.map(escapeHtml).join(', ');
|
|
97
|
+
const label = row.minimal ? ui.minimalCoalition : ui.surplusCoalition;
|
|
98
|
+
return `<tr><th scope="row"><span class="row-badge ${row.minimal ? 'is-minimal' : ''}">${escapeHtml(label)}</span><span>${members}</span></th><td>${number(row.seats)}</td><td>+${number(row.surplus)}</td><td>${redundant}</td></tr>`;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function renderPivots(analysis: CoalitionAnalysis, ui: CoalitionMajorityUI): string {
|
|
102
|
+
return analysis.parties.map((party) => {
|
|
103
|
+
const count = analysis.pivotCounts[party.name] ?? 0;
|
|
104
|
+
const share = analysis.pivotShares[party.name] ?? 0;
|
|
105
|
+
return `<li><span>${escapeHtml(party.name)}</span><strong>${number(count)}</strong><small>${percent(share)} ${escapeHtml(ui.pivotal)}</small></li>`;
|
|
106
|
+
}).join('');
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function filterRows(rows: CoalitionRecord[], filter: CoalitionFilter): CoalitionRecord[] {
|
|
110
|
+
if (filter === 'minimal') return rows.filter((row) => row.minimal);
|
|
111
|
+
if (filter === 'surplus') return rows.filter((row) => !row.minimal);
|
|
112
|
+
return rows;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function clearResults(targets: ResultTargets, label: string): void {
|
|
116
|
+
targets.status.textContent = label;
|
|
117
|
+
targets.stats.innerHTML = '';
|
|
118
|
+
targets.pivots.innerHTML = '';
|
|
119
|
+
targets.table.innerHTML = '';
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function resultLabel(analysis: CoalitionAnalysis, ui: CoalitionMajorityUI): string {
|
|
123
|
+
if (analysis.warnings[0]) return analysis.warnings[0];
|
|
124
|
+
if (analysis.winningCoalitions > 0) return `${number(analysis.winningCoalitions)} ${ui.winningCoalitions.toLowerCase()}`;
|
|
125
|
+
return ui.noPivots;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function getResultTargets(root: HTMLElement): ResultTargets | undefined {
|
|
129
|
+
const status = root.querySelector<HTMLElement>('[data-coalition-status]');
|
|
130
|
+
const stats = root.querySelector<HTMLElement>('[data-coalition-stats]');
|
|
131
|
+
const pivots = root.querySelector<HTMLElement>('[data-coalition-pivots]');
|
|
132
|
+
const table = root.querySelector<HTMLTableSectionElement>('[data-coalition-rows]');
|
|
133
|
+
return status && stats && pivots && table ? { status, stats, pivots, table } : undefined;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
export function renderCoalitionResults(root: HTMLElement, analysis: CoalitionAnalysis, ui: CoalitionMajorityUI, filter: CoalitionFilter = 'all'): void {
|
|
137
|
+
const targets = getResultTargets(root);
|
|
138
|
+
if (!targets) return;
|
|
139
|
+
if (!analysis.valid) return clearResults(targets, analysis.error ?? 'Check the inputs.');
|
|
140
|
+
targets.status.textContent = resultLabel(analysis, ui);
|
|
141
|
+
targets.stats.innerHTML = [stat(ui.chamberSeats, number(analysis.totalSeats)), stat(ui.threshold, number(analysis.threshold)), stat(ui.totalCoalitions, number(analysis.totalCoalitions)), stat(ui.minimalCoalition, number(analysis.minimalWinningCoalitions)), stat(ui.criticalDefections, number(analysis.pivotTotal))].join('');
|
|
142
|
+
targets.pivots.innerHTML = renderPivots(analysis, ui);
|
|
143
|
+
const rows = filterRows(analysis.displayedCoalitions, filter);
|
|
144
|
+
targets.table.innerHTML = rows.length === 0 ? `<tr><td colspan="4">${escapeHtml(ui.noMatchingCoalitions)}</td></tr>` : rows.map((row) => coalitionRow(row, ui)).join('');
|
|
145
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { CivicToolEntry, ToolLocaleContent } from '../../types';
|
|
2
|
+
import type { CoalitionMajorityUI } from './ui';
|
|
3
|
+
|
|
4
|
+
export type { CoalitionMajorityUI } from './ui';
|
|
5
|
+
export type CoalitionMajorityLocaleContent = ToolLocaleContent<CoalitionMajorityUI>;
|
|
6
|
+
|
|
7
|
+
export const coalitionMajorityCalculator: CivicToolEntry<CoalitionMajorityUI> = {
|
|
8
|
+
id: 'coalition-majority-calculator',
|
|
9
|
+
phase: 'localized',
|
|
10
|
+
icons: { bg: 'mdi:bank', fg: 'mdi:account-group' },
|
|
11
|
+
i18n: {
|
|
12
|
+
en: () => import('./i18n/en').then((module) => module.content),
|
|
13
|
+
de: () => import('./i18n/de').then((module) => module.content),
|
|
14
|
+
es: () => import('./i18n/es').then((module) => module.content),
|
|
15
|
+
fr: () => import('./i18n/fr').then((module) => module.content),
|
|
16
|
+
id: () => import('./i18n/id').then((module) => module.content),
|
|
17
|
+
it: () => import('./i18n/it').then((module) => module.content),
|
|
18
|
+
ja: () => import('./i18n/ja').then((module) => module.content),
|
|
19
|
+
ko: () => import('./i18n/ko').then((module) => module.content),
|
|
20
|
+
nl: () => import('./i18n/nl').then((module) => module.content),
|
|
21
|
+
pl: () => import('./i18n/pl').then((module) => module.content),
|
|
22
|
+
pt: () => import('./i18n/pt').then((module) => module.content),
|
|
23
|
+
ru: () => import('./i18n/ru').then((module) => module.content),
|
|
24
|
+
sv: () => import('./i18n/sv').then((module) => module.content),
|
|
25
|
+
tr: () => import('./i18n/tr').then((module) => module.content),
|
|
26
|
+
zh: () => import('./i18n/zh').then((module) => module.content),
|
|
27
|
+
},
|
|
28
|
+
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { CoalitionAnalysis } from './logic';
|
|
2
|
+
|
|
3
|
+
export interface CoalitionStatus {
|
|
4
|
+
tone: 'good' | 'warn' | 'neutral';
|
|
5
|
+
label: string;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export function evaluateCoalitions(analysis: CoalitionAnalysis): CoalitionStatus {
|
|
9
|
+
if (!analysis.valid) return { tone: 'warn', label: analysis.error ?? 'Check the inputs.' };
|
|
10
|
+
if (analysis.winningCoalitions === 0) return { tone: 'warn', label: 'No winning coalition exists.' };
|
|
11
|
+
if (analysis.minimalWinningCoalitions === 0) return { tone: 'neutral', label: 'Winning coalitions exist, but none is minimal.' };
|
|
12
|
+
return { tone: 'good', label: `${analysis.minimalWinningCoalitions} minimal winning coalition${analysis.minimalWinningCoalitions === 1 ? '' : 's'} found.` };
|
|
13
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import type { FAQPage, HowTo, SoftwareApplication } from 'schema-dts';
|
|
2
|
+
import { bibliography } from '../bibliography';
|
|
3
|
+
import type { CoalitionMajorityLocaleContent } from '../entry';
|
|
4
|
+
import type { CoalitionMajorityUI } from '../ui';
|
|
5
|
+
|
|
6
|
+
const ui: CoalitionMajorityUI = {
|
|
7
|
+
addParty: 'Partei hinzufügen', allCoalitions: 'Alle siegreichen Koalitionen', calculate: 'Koalitionen berechnen', chamberSeats: 'Sitze insgesamt', coalitionSceneLabel: 'Koalitionskonstellation', criticalDefections: 'Kritische Austritte', emptyState: 'Füge mindestens zwei Parteien hinzu und trage ihre Sitze ein.', explanation: 'Das Modell nimmt an, dass jede aufgeführte Partei jeder Kombination beitreten kann. Es misst Sitzarithmetik, nicht politische Vereinbarkeit.', edgeCasesText: 'Leere oder doppelte Namen werden eindeutig gemacht. Parteien mit null Sitzen können nie entscheidend sein. Über 20 Parteien wird die exakte Suche beendet.', edgeCasesTitle: 'Grenzfälle und Warnungen', filterCoalitions: 'Anzeigen', maximumParties: 'Maximal 20 Parteien, weil die vollständige Aufzählung mit 2 hoch N wächst.', majorityThreshold: 'Mehrheitsschwelle', methodTitle: 'Angewandtes Modell', methodText: 'Jede nicht leere Teilmenge wird geprüft. Eine Koalition gewinnt ab der angegebenen Schwelle. Eine Partei ist entscheidend, wenn ihr Austritt eine gewinnende Koalition verlieren lässt.', minimalCoalition: 'Minimale Gewinnkoalition', noMatchingCoalitions: 'Keine Koalition passt zu diesem Filter.', noPivots: 'Unter dieser Schwelle ist keine Partei entscheidend.', none: 'Keine', notDoText: 'Das Modell bewertet weder Ideologie, politische Vereinbarkeit, Verhandlungen, Legitimität, Stabilität noch die Wahrscheinlichkeit einer Regierungsbildung.', notDoTitle: 'Was dieses Tool nicht leistet', partyName: 'Parteiname', partySeats: 'Sitze', partyTable: 'Koalitionsdetails', pivotal: 'entscheidend', pivotalParty: 'Entscheidende Partei', redundantMembers: 'Überflüssige Mitglieder', removeParty: 'Partei entfernen', reset: 'Beispiel zurücksetzen', resultTitle: 'Koalitionskarte', seats: 'Sitze', surplus: 'Überschuss', surplusCoalition: 'Koalition mit Überschuss', threshold: 'Schwelle', thresholdHint: 'Eine Koalition gewinnt, sobald ihre Sitze diese Schwelle erreichen oder überschreiten.', totalCoalitions: 'Mögliche nicht leere Koalitionen', winningCoalitions: 'Gewinnende Koalitionen',
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
const softwareApplication: SoftwareApplication = { '@type': 'SoftwareApplication', name: 'Rechner für Koalitionsmehrheiten', applicationCategory: 'EducationalApplication', operatingSystem: 'Any', description: 'Sitzbasierte Koalitionen, minimale Gewinnkoalitionen und entscheidende Parteien unter einer festgelegten Schwelle auflisten.', url: 'https://gamebob.dev/de/koalitionsmehrheit-rechner', offers: { '@type': 'Offer', price: '0', priceCurrency: 'EUR' } };
|
|
11
|
+
const faqPage: FAQPage = { '@type': 'FAQPage', mainEntity: [
|
|
12
|
+
{ '@type': 'Question', name: 'Wann ist eine Koalition minimal?', acceptedAnswer: { '@type': 'Answer', text: 'Sie ist minimal, wenn der Austritt jedes einzelnen Mitglieds die Koalition unter die festgelegte Sitzschwelle bringt. Eine Koalition mit Überschuss bleibt auch nach dem Austritt mindestens eines Mitglieds siegreich.' } },
|
|
13
|
+
{ '@type': 'Question', name: 'Was bedeutet entscheidend?', acceptedAnswer: { '@type': 'Answer', text: 'Eine Partei wird jedes Mal als entscheidend gezählt, wenn ihr Austritt eine bestimmte gewinnende Koalition in eine verlierende Koalition verwandelt. Ihr Anteil wird durch alle kritischen Austritte geteilt.' } },
|
|
14
|
+
{ '@type': 'Question', name: 'Sagt der Rechner eine echte Regierung voraus?', acceptedAnswer: { '@type': 'Answer', text: 'Nein. Er bewertet nur erklärte Sitzgewichte und eine Schwelle. Ideologie, Verhandlungen, Minderheitsunterstützung und Verfassungsregeln liegen außerhalb des Modells.' } },
|
|
15
|
+
{ '@type': 'Question', name: 'Warum gibt es eine Grenze von 20 Parteien?', acceptedAnswer: { '@type': 'Answer', text: 'Das Tool prüft jede nicht leere Teilmenge. Bei 20 Parteien entstehen bereits 1.048.575 Kombinationen, was eine praktikable Grenze im Browser ist.' } },
|
|
16
|
+
] };
|
|
17
|
+
const howTo: HowTo = { '@type': 'HowTo', name: 'Eine gewichtete Mehrheitskoalition untersuchen', step: [
|
|
18
|
+
{ '@type': 'HowToStep', name: 'Parteien eingeben', text: 'Füge jede Partei hinzu und trage ihr Sitzgewicht ein. Eindeutige Namen erleichtern die Prüfung.' },
|
|
19
|
+
{ '@type': 'HowToStep', name: 'Schwelle festlegen', text: 'Gib die Sitzzahl ein, die eine Koalition erreichen muss.' },
|
|
20
|
+
{ '@type': 'HowToStep', name: 'Konstellation prüfen', text: 'Achte auf Parteien mit Akzentring und vergleiche ihre entscheidenden Zählungen mit ihrer Sitzzahl.' },
|
|
21
|
+
{ '@type': 'HowToStep', name: 'Tabelle lesen', text: 'Vergleiche minimale Gewinnkoalitionen, Überschuss, und überflüssige Mitglieder.' },
|
|
22
|
+
{ '@type': 'HowToStep', name: 'Szenario verändern', text: 'Ändere jeweils nur eine Sitzzahl oder die Schwelle, damit die Ursache neuer Koalitionen sichtbar bleibt.' },
|
|
23
|
+
] };
|
|
24
|
+
|
|
25
|
+
export const content: CoalitionMajorityLocaleContent = { slug: 'koalitionsmehrheit-rechner', title: 'Rechner für Koalitionsmehrheiten', description: 'Untersuche jede sitzbasierte Koalition unter einer festgelegten Mehrheitsschwelle und finde minimale Gewinnkoalitionen sowie entscheidende Parteien.', ui, seo: [
|
|
26
|
+
{ type: 'title', text: 'Welche Sitzkombinationen eine Mehrheit erreichen', level: 2 },
|
|
27
|
+
{ type: 'paragraph', html: 'Eine Koalition gewinnt, wenn die Sitze ihrer Mitglieder mindestens die von dir festgelegte Schwelle erreichen. Dieser Rechner prüft jede nicht leere Kombination und zeigt den genauen Überschuss. So lässt sich nachvollziehen, warum eine Gruppe gewinnt.' },
|
|
28
|
+
{ type: 'title', text: 'Minimale Koalitionen und Koalitionen mit Überschuss', level: 2 },
|
|
29
|
+
{ type: 'paragraph', html: 'Eine minimale Gewinnkoalition verliert ihre Mehrheit, sobald ein beliebiges Mitglied austritt. Eine Koalition mit Überschuss bleibt nach dem Austritt eines oder mehrerer Mitglieder siegreich. Das ist eine strukturelle Aussage über Sitze, keine Vorhersage politischer Stabilität.' },
|
|
30
|
+
{ type: 'table', headers: ['Ergebnis', 'Prüfung', 'Erkenntnis'], rows: [['Gewinnende Koalition', 'Die Sitze erreichen die Schwelle.', 'Welche Gruppen rechnerisch gewinnen können.'], ['Minimale Gewinnkoalition', 'Jedes Mitglied ist für den Sieg entscheidend.', 'Welche Gruppe keinen Sitzüberschuss hat.'], ['Koalition mit Überschuss', 'Mindestens ein Mitglied kann ausscheiden und die Gruppe gewinnt weiter.', 'Wo eine Mehrheit redundante Partner hat.']] },
|
|
31
|
+
{ type: 'title', text: 'Entscheidende Parteien als Sitzarithmetik lesen', level: 2 },
|
|
32
|
+
{ type: 'paragraph', html: 'Die entscheidende Zählung folgt einer Banzhaf-artigen kritischen Austrittsprüfung. Eine Partei zählt, wenn ihr Austritt eine bestimmte gewinnende Koalition verlieren lässt. Der Anteil wird über alle kritischen Austritte normalisiert.' },
|
|
33
|
+
{ type: 'list', items: ['Beginne mit der Gesamtzahl der Sitze und verwende die Schwelle deiner Regel.', 'Halte die Schwelle fest, wenn du zwei Sitzverteilungen vergleichst.', 'Prüfe überflüssige Mitglieder in der Tabelle, bevor du die Liste entscheidender Parteien deutest.', 'Ändere beim Sensitivitätstest jeweils nur eine Partei.', 'Behandle null Sitze und doppelte Namen als Datenprobleme, nicht als politische Aussagen.'] },
|
|
34
|
+
{ type: 'title', text: 'Eine begrenzte exakte Suche', level: 2 },
|
|
35
|
+
{ type: 'paragraph', html: 'Bei N Parteien bewertet der Rechner 2 hoch N minus 1 nicht leere Koalitionen. Die Grenze von 20 Parteien hält die vollständige Suche im Browser nachvollziehbar. Die Tabelle zeigt eine lesbare Auswahl, während die Summen die gesamte Suche umfassen.' },
|
|
36
|
+
{ type: 'tip', title: 'Schwellen sind Annahmen, keine Gesetze', html: 'Der Rechner erkennt keine landesspezifische Mehrheit, Investiturregel oder Vertrauensvereinbarung. Gib die Schwelle selbst an und bewahre die Regel neben dem Ergebnis auf.' },
|
|
37
|
+
{ type: 'title', text: 'Was das Modell nicht ableiten kann', level: 2 },
|
|
38
|
+
{ type: 'paragraph', html: 'Sitzgewichte beschreiben nur die Fähigkeit einer Gruppe, eine Zahlenschwelle zu überschreiten. Das Modell bewertet keine Ideologie, politische Distanz, Kabinettsverhandlungen, Legitimität, Stabilität oder Überlebenswahrscheinlichkeit einer Regierung.' },
|
|
39
|
+
{ type: 'tip', title: 'Nutze das Ergebnis als Prüfspur', html: 'Wenn eine Partei entscheidend ist, suche eine gewinnende Koalition, in der ihr Austritt die Schwelle unterschreitet. Bei Überschusskoalitionen zeigen die überflüssigen Mitglieder, wo Sitzreserven liegen.' },
|
|
40
|
+
], faq: [
|
|
41
|
+
{ question: 'Wann ist eine Koalition minimal?', answer: 'Wenn der Austritt jedes Mitglieds sie verlieren lässt. Kann mindestens ein Mitglied austreten und die Schwelle bleibt erreicht, handelt es sich um eine Koalition mit Überschuss.' },
|
|
42
|
+
{ question: 'Was bedeutet entscheidend?', answer: 'Eine Partei ist für jede gewinnende Koalition entscheidend, die ohne sie verliert. Ihr Anteil ist ihre kritische Zählung geteilt durch die Gesamtzahl kritischer Austritte.' },
|
|
43
|
+
{ question: 'Sagt das eine echte Regierung voraus?', answer: 'Nein. Der Rechner bewertet nur Sitzarithmetik. Politische Vereinbarkeit, Verhandlungen und Verfassungsregeln gehören nicht zum Modell.' },
|
|
44
|
+
{ question: 'Warum sind es höchstens 20 Parteien?', answer: 'Die exakte Suche prüft 2 hoch N minus 1 Kombinationen. Bei 20 Parteien sind das bereits 1.048.575 Kombinationen.' },
|
|
45
|
+
], bibliography, howTo: [
|
|
46
|
+
{ name: 'Parteien eingeben', text: 'Füge jede Partei hinzu und trage ihr Sitzgewicht mit einem eindeutigen Namen ein.' },
|
|
47
|
+
{ name: 'Schwelle festlegen', text: 'Gib die Sitzzahl ein, die eine Koalition zum Sieg benötigt.' },
|
|
48
|
+
{ name: 'Entscheidende Parteien prüfen', text: 'Nutze Akzentringe und Zählungen, um notwendige Parteien zu erkennen.' },
|
|
49
|
+
{ name: 'Tabelle lesen', text: 'Vergleiche minimale Gruppen, Überschuss und überflüssige Mitglieder.' },
|
|
50
|
+
{ name: 'Eine Annahme ändern', text: 'Passe jeweils nur eine Sitzzahl oder die Schwelle an.' },
|
|
51
|
+
], schemas: [softwareApplication, faqPage, howTo] as unknown as Record<string, unknown>[] };
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import type { FAQPage, HowTo, SoftwareApplication } from 'schema-dts';
|
|
2
|
+
import { bibliography } from '../bibliography';
|
|
3
|
+
import type { CoalitionMajorityLocaleContent } from '../entry';
|
|
4
|
+
import { ui } from '../ui';
|
|
5
|
+
|
|
6
|
+
const softwareApplication: SoftwareApplication = {
|
|
7
|
+
'@type': 'SoftwareApplication',
|
|
8
|
+
name: 'Coalition Majority Calculator',
|
|
9
|
+
applicationCategory: 'EducationalApplication',
|
|
10
|
+
operatingSystem: 'Any',
|
|
11
|
+
description: 'Enumerate seat based coalitions, minimal winning groups, and pivotal parties under a declared threshold.',
|
|
12
|
+
url: 'https://gamebob.dev/en/coalition-majority-calculator',
|
|
13
|
+
offers: { '@type': 'Offer', price: '0', priceCurrency: 'USD' },
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
const faqPage: FAQPage = {
|
|
17
|
+
'@type': 'FAQPage',
|
|
18
|
+
mainEntity: [
|
|
19
|
+
{ '@type': 'Question', name: 'What makes a coalition minimal?', acceptedAnswer: { '@type': 'Answer', text: 'A winning coalition is minimal when removing any one of its members makes the coalition fall below the declared seat threshold. A surplus coalition still wins after at least one member leaves.' } },
|
|
20
|
+
{ '@type': 'Question', name: 'What does pivotal mean here?', acceptedAnswer: { '@type': 'Answer', text: 'A party is counted as pivotal each time it belongs to a winning coalition that would become losing without it. The displayed share is its count divided by all such critical defections.' } },
|
|
21
|
+
{ '@type': 'Question', name: 'Does the calculator predict a real government?', acceptedAnswer: { '@type': 'Answer', text: 'No. It only evaluates declared seat weights and a threshold. It does not know ideology, policy compatibility, bargaining, confidence agreements, minority support, or constitutional rules.' } },
|
|
22
|
+
{ '@type': 'Question', name: 'Why is there a limit of 20 parties?', acceptedAnswer: { '@type': 'Answer', text: 'The tool checks every non empty subset, so the number of possible coalitions is 2 to the power of N minus 1. Twenty parties already create 1,048,575 non empty combinations, which is a practical browser boundary.' } },
|
|
23
|
+
],
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
const howTo: HowTo = {
|
|
27
|
+
'@type': 'HowTo',
|
|
28
|
+
name: 'Explore a weighted majority coalition',
|
|
29
|
+
step: [
|
|
30
|
+
{ '@type': 'HowToStep', name: 'Enter the parties', text: 'Add each party and enter its seat weight. Keep names distinct so the result remains easy to audit.' },
|
|
31
|
+
{ '@type': 'HowToStep', name: 'Set the threshold', text: 'Enter the number of seats a coalition must reach. The threshold is visible again in the result.' },
|
|
32
|
+
{ '@type': 'HowToStep', name: 'Inspect the constellation', text: 'Look for parties with an accent ring and compare their pivotal counts with their seat totals.' },
|
|
33
|
+
{ '@type': 'HowToStep', name: 'Read the coalition table', text: 'Compare minimal winning coalitions with surplus coalitions, their seat surplus, and their redundant members.' },
|
|
34
|
+
{ '@type': 'HowToStep', name: 'Test another scenario', text: 'Change one seat total or the threshold at a time to see which coalitions appear or disappear.' },
|
|
35
|
+
],
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
export const content: CoalitionMajorityLocaleContent = {
|
|
39
|
+
slug: 'coalition-majority-calculator',
|
|
40
|
+
title: 'Coalition Majority Calculator',
|
|
41
|
+
description: 'Explore every seat based coalition under a declared majority threshold. Find minimal winning groups, surplus seats, and parties that are pivotal in the arithmetic.',
|
|
42
|
+
ui,
|
|
43
|
+
seo: [
|
|
44
|
+
{ type: 'title', text: 'See Which Seat Combinations Can Reach a Majority', level: 2 },
|
|
45
|
+
{ type: 'paragraph', html: 'A coalition is winning when the seats held by its members meet or exceed the threshold you declare. This calculator enumerates each non empty combination of parties, reports the groups that can win, and shows the exact surplus above the threshold. Because the result is built from explicit seat weights, you can inspect why a group wins instead of relying on a vague ranking.' },
|
|
46
|
+
{ type: 'title', text: 'Minimal Winning and Surplus Coalitions', level: 2 },
|
|
47
|
+
{ type: 'paragraph', html: 'A minimal winning coalition loses its majority when any one member leaves. A surplus coalition remains above the threshold after one or more members leave, so it contains redundancy under this seat rule. The distinction is structural: it does not say that a party would actually leave or that a minimal coalition would be politically stable.' },
|
|
48
|
+
{ type: 'table', headers: ['Result', 'Test applied', 'What it helps you see'], rows: [['Winning coalition', 'Member seats are at least the threshold.', 'Which groups have enough seats in principle.'], ['Minimal winning coalition', 'Every member is critical to staying above the threshold.', 'Which groups have no seat arithmetic surplus.'], ['Surplus coalition', 'At least one member can be removed and the group still wins.', 'Where a majority has redundant partners.']] },
|
|
49
|
+
{ type: 'title', text: 'Read Pivotal Parties as Seat Arithmetic', level: 2 },
|
|
50
|
+
{ type: 'paragraph', html: 'The pivotal count uses a Banzhaf style critical defection: a party is counted when removing it changes a particular winning coalition into a losing one. The share is normalized across all critical defections in the scenario. A party can therefore have more pivotal influence than its seat share suggests, while another party can hold seats without ever being necessary in a winning group.' },
|
|
51
|
+
{ type: 'list', items: ['Start with the chamber seat total and use the threshold that matches your declared rule.', 'Keep the same threshold while comparing two seat distributions.', 'Use the coalition table to inspect redundant members before drawing conclusions from the pivot list.', 'Change one party at a time when exploring sensitivity so the cause of a new coalition remains visible.', 'Treat zero seat parties and duplicated labels as data quality issues, not as political conclusions.'] },
|
|
52
|
+
{ type: 'title', text: 'A Bounded Exact Search', level: 2 },
|
|
53
|
+
{ type: 'paragraph', html: 'For N parties, the calculator evaluates 2 to the power of N minus 1 non empty coalitions. The limit of 20 parties keeps exhaustive enumeration transparent and usable in a browser. The table shows a readable subset of the winning groups while the summary counts are calculated over the complete search.' },
|
|
54
|
+
{ type: 'tip', title: 'Thresholds are assumptions, not laws', html: 'The calculator does not identify a country rule, an investiture requirement, a confidence agreement, or a valid parliamentary majority. Enter the threshold yourself and preserve the rule beside any saved result. Real coalition formation also depends on preferences, negotiation, institutions, and support from outside the listed group.' },
|
|
55
|
+
{ type: 'title', text: 'What the Model Cannot Infer', level: 2 },
|
|
56
|
+
{ type: 'paragraph', html: 'Seat weights describe the capacity of a group to cross a numerical threshold, not the likelihood that parties will cooperate. The model does not assess ideology, policy distance, cabinet bargaining, discipline, legitimacy, stability, or the probability of a government surviving. It also does not account for absent members, internal factions, constitutional exceptions, or votes that require a different quorum.' },
|
|
57
|
+
{ type: 'tip', title: 'Use the result as an audit trail', html: 'When a party is marked pivotal, find one of the listed winning coalitions where its removal crosses the threshold. When a coalition is marked surplus, inspect its redundant members. These checks make the arithmetic reproducible without turning it into a forecast.' },
|
|
58
|
+
],
|
|
59
|
+
faq: [
|
|
60
|
+
{ question: 'What makes a coalition minimal?', answer: 'A winning coalition is minimal when removing any one member makes it lose. If at least one member can leave while the coalition still reaches the threshold, it is surplus.' },
|
|
61
|
+
{ question: 'What does pivotal mean here?', answer: 'A party is pivotal for every winning coalition that becomes losing when that party is removed. The share is its critical count divided by the total critical count.' },
|
|
62
|
+
{ question: 'Does this predict a real government?', answer: 'No. It evaluates seat arithmetic only. Political compatibility, bargaining, minority support, constitutional rules, and confidence agreements are outside the model.' },
|
|
63
|
+
{ question: 'Why is the limit 20 parties?', answer: 'The exact search checks 2 to the power of N minus 1 non empty combinations. Twenty parties already produce 1,048,575 combinations.' },
|
|
64
|
+
],
|
|
65
|
+
bibliography,
|
|
66
|
+
howTo: [
|
|
67
|
+
{ name: 'Enter the parties', text: 'Add each party and enter its seat weight with a distinct name.' },
|
|
68
|
+
{ name: 'Set the threshold', text: 'Enter the number of seats needed for a coalition to win.' },
|
|
69
|
+
{ name: 'Inspect pivotal parties', text: 'Use the accent rings and pivotal counts to identify parties that are necessary in winning groups.' },
|
|
70
|
+
{ name: 'Read the table', text: 'Compare minimal winning groups with surplus groups and their redundant members.' },
|
|
71
|
+
{ name: 'Change one assumption', text: 'Adjust one seat total or the threshold at a time to keep the scenario interpretable.' },
|
|
72
|
+
],
|
|
73
|
+
schemas: [softwareApplication, faqPage, howTo] as unknown as Record<string, unknown>[],
|
|
74
|
+
};
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import type { FAQPage, HowTo, SoftwareApplication } from 'schema-dts';
|
|
2
|
+
import { bibliography } from '../bibliography';
|
|
3
|
+
import type { CoalitionMajorityLocaleContent } from '../entry';
|
|
4
|
+
import type { CoalitionMajorityUI } from '../ui';
|
|
5
|
+
|
|
6
|
+
const ui: CoalitionMajorityUI = {
|
|
7
|
+
addParty: 'Añadir partido', allCoalitions: 'Todas las coaliciones ganadoras', calculate: 'Calcular coaliciones', chamberSeats: 'Escaños totales', coalitionSceneLabel: 'Constelación de coaliciones', criticalDefections: 'Salidas críticas', emptyState: 'Añade al menos dos partidos e introduce sus escaños.', explanation: 'El modelo supone que cada partido puede unirse a cualquier combinación. Mide aritmética de escaños, no compatibilidad política.', edgeCasesText: 'Los nombres vacíos o duplicados se hacen únicos. Un partido con cero escaños nunca puede ser decisivo. Con más de 20 partidos se detiene la búsqueda exacta.', edgeCasesTitle: 'Casos límite y avisos', filterCoalitions: 'Mostrar', maximumParties: 'Máximo 20 partidos porque la enumeración exhaustiva crece como 2 elevado a N.', majorityThreshold: 'Umbral de mayoría', methodTitle: 'Modelo aplicado', methodText: 'Se comprueba cada subconjunto no vacío. Una coalición gana al alcanzar el umbral declarado. Un partido es decisivo si al retirarlo una coalición ganadora pierde.', minimalCoalition: 'Coalición mínima ganadora', noMatchingCoalitions: 'Ninguna coalición coincide con este filtro.', noPivots: 'Ningún partido es decisivo con este umbral.', none: 'Ninguno', notDoText: 'No evalúa ideología, compatibilidad política, negociación, legitimidad, estabilidad ni si llegará a formarse un gobierno.', notDoTitle: 'Lo que no hace esta herramienta', partyName: 'Nombre del partido', partySeats: 'Escaños', partyTable: 'Detalles de coalición', pivotal: 'decisivo', pivotalParty: 'Partido decisivo', redundantMembers: 'Miembros redundantes', removeParty: 'Eliminar partido', reset: 'Restablecer ejemplo', resultTitle: 'Mapa de coaliciones', seats: 'escaños', surplus: 'excedente', surplusCoalition: 'Coalición con excedente', threshold: 'Umbral', thresholdHint: 'Una coalición gana cuando sus escaños alcanzan o superan este umbral.', totalCoalitions: 'Coaliciones no vacías posibles', winningCoalitions: 'Coaliciones ganadoras',
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
const softwareApplication: SoftwareApplication = { '@type': 'SoftwareApplication', name: 'Calculadora de mayoría de coaliciones', applicationCategory: 'EducationalApplication', operatingSystem: 'Any', description: 'Enumera coaliciones por escaños, coaliciones mínimas ganadoras y partidos decisivos bajo un umbral declarado.', url: 'https://gamebob.dev/es/calculadora-mayoria-coaliciones', offers: { '@type': 'Offer', price: '0', priceCurrency: 'EUR' } };
|
|
11
|
+
const faqPage: FAQPage = { '@type': 'FAQPage', mainEntity: [
|
|
12
|
+
{ '@type': 'Question', name: '¿Cuándo es mínima una coalición?', acceptedAnswer: { '@type': 'Answer', text: 'Es mínima cuando retirar a cualquiera de sus miembros hace que baje del umbral de escaños declarado. Una coalición con excedente sigue ganando aunque se retire al menos un miembro.' } },
|
|
13
|
+
{ '@type': 'Question', name: '¿Qué significa decisivo?', acceptedAnswer: { '@type': 'Answer', text: 'Un partido se cuenta como decisivo cada vez que retirarlo convierte una coalición ganadora en perdedora. Su porcentaje divide su cuenta entre todas las salidas críticas.' } },
|
|
14
|
+
{ '@type': 'Question', name: '¿La calculadora predice un gobierno real?', acceptedAnswer: { '@type': 'Answer', text: 'No. Solo evalúa pesos de escaños y un umbral declarados. La ideología, la negociación, el apoyo minoritario y las reglas constitucionales quedan fuera.' } },
|
|
15
|
+
{ '@type': 'Question', name: '¿Por qué hay un límite de 20 partidos?', acceptedAnswer: { '@type': 'Answer', text: 'La herramienta comprueba cada subconjunto no vacío. Veinte partidos ya producen 1.048.575 combinaciones, un límite práctico para el navegador.' } },
|
|
16
|
+
] };
|
|
17
|
+
const howTo: HowTo = { '@type': 'HowTo', name: 'Explorar una mayoría ponderada', step: [
|
|
18
|
+
{ '@type': 'HowToStep', name: 'Introduce los partidos', text: 'Añade cada partido e indica sus escaños. Usa nombres distintos para auditar el resultado.' },
|
|
19
|
+
{ '@type': 'HowToStep', name: 'Fija el umbral', text: 'Escribe cuántos escaños debe alcanzar una coalición para ganar.' },
|
|
20
|
+
{ '@type': 'HowToStep', name: 'Mira la constelación', text: 'Observa los anillos de acento y compara el recuento decisivo con los escaños.' },
|
|
21
|
+
{ '@type': 'HowToStep', name: 'Lee la tabla', text: 'Compara coaliciones mínimas, excedente y miembros redundantes.' },
|
|
22
|
+
{ '@type': 'HowToStep', name: 'Prueba otro escenario', text: 'Cambia un escaño o el umbral cada vez para entender qué coaliciones aparecen.' },
|
|
23
|
+
] };
|
|
24
|
+
|
|
25
|
+
export const content: CoalitionMajorityLocaleContent = { slug: 'calculadora-mayoria-coaliciones', title: 'Calculadora de mayoría de coaliciones', description: 'Explora cada coalición por escaños con un umbral de mayoría declarado. Encuentra grupos mínimos ganadores, excedente y partidos decisivos.', ui, seo: [
|
|
26
|
+
{ type: 'title', text: 'Qué combinaciones de escaños alcanzan una mayoría', level: 2 },
|
|
27
|
+
{ type: 'paragraph', html: 'Una coalición gana cuando los escaños de sus miembros alcanzan o superan el umbral que declaras. Esta calculadora enumera cada combinación no vacía y muestra el excedente exacto sobre el umbral. Así puedes revisar por qué gana un grupo.' },
|
|
28
|
+
{ type: 'title', text: 'Coaliciones mínimas y coaliciones con excedente', level: 2 },
|
|
29
|
+
{ type: 'paragraph', html: 'Una coalición mínima ganadora pierde la mayoría si se marcha cualquier miembro. Una coalición con excedente sigue por encima del umbral aunque se retire uno o más miembros. Es una distinción estructural de escaños, no una predicción de estabilidad política.' },
|
|
30
|
+
{ type: 'table', headers: ['Resultado', 'Prueba aplicada', 'Qué permite ver'], rows: [['Coalición ganadora', 'Sus escaños alcanzan el umbral.', 'Qué grupos tienen escaños suficientes en principio.'], ['Coalición mínima ganadora', 'Cada miembro es necesario para seguir ganando.', 'Qué grupos no tienen excedente aritmético.'], ['Coalición con excedente', 'Se puede retirar al menos un miembro y sigue ganando.', 'Dónde una mayoría tiene socios redundantes.']] },
|
|
31
|
+
{ type: 'title', text: 'Leer los partidos decisivos como aritmética', level: 2 },
|
|
32
|
+
{ type: 'paragraph', html: 'El recuento decisivo usa una prueba de salida crítica inspirada en Banzhaf: un partido cuenta cuando retirarlo convierte una coalición ganadora en perdedora. El porcentaje se normaliza entre todas las salidas críticas del escenario.' },
|
|
33
|
+
{ type: 'list', items: ['Empieza con el total de escaños y usa el umbral de tu regla.', 'Mantén el mismo umbral al comparar dos repartos de escaños.', 'Mira los miembros redundantes en la tabla antes de interpretar la lista decisiva.', 'Cambia un partido cada vez para entender la sensibilidad del resultado.', 'Trata los nombres duplicados y los partidos sin escaños como problemas de datos.'] },
|
|
34
|
+
{ type: 'title', text: 'Una búsqueda exacta con límite', level: 2 },
|
|
35
|
+
{ type: 'paragraph', html: 'Para N partidos, la calculadora evalúa 2 elevado a N menos 1 coaliciones no vacías. El límite de 20 mantiene la búsqueda exhaustiva comprensible y usable en el navegador. La tabla muestra un subconjunto legible y los totales cubren toda la búsqueda.' },
|
|
36
|
+
{ type: 'tip', title: 'Los umbrales son supuestos, no leyes', html: 'La calculadora no identifica una regla constitucional, una investidura ni una mayoría parlamentaria válida. Introduce el umbral y conserva esa regla junto al resultado.' },
|
|
37
|
+
{ type: 'title', text: 'Lo que el modelo no puede inferir', level: 2 },
|
|
38
|
+
{ type: 'paragraph', html: 'Los escaños describen la capacidad de cruzar una cifra, no la probabilidad de cooperar. El modelo no valora ideología, distancia programática, negociación, legitimidad, estabilidad, facciones internas ni apoyo externo.' },
|
|
39
|
+
{ type: 'tip', title: 'Usa el resultado como rastro de auditoría', html: 'Cuando un partido aparece como decisivo, localiza una coalición ganadora donde su salida cruce el umbral. Cuando una coalición tiene excedente, revisa sus miembros redundantes.' },
|
|
40
|
+
], faq: [
|
|
41
|
+
{ question: '¿Cuándo es mínima una coalición?', answer: 'Cuando retirar a cualquiera de sus miembros hace que pierda. Si al menos uno puede salir y todavía alcanza el umbral, tiene excedente.' },
|
|
42
|
+
{ question: '¿Qué significa decisivo?', answer: 'Un partido es decisivo en cada coalición ganadora que se vuelve perdedora al retirarlo. Su porcentaje es su cuenta crítica dividida por el total crítico.' },
|
|
43
|
+
{ question: '¿Predice un gobierno real?', answer: 'No. Solo evalúa aritmética de escaños. La compatibilidad política, la negociación y las reglas constitucionales quedan fuera.' },
|
|
44
|
+
{ question: '¿Por qué el límite es 20 partidos?', answer: 'La búsqueda exacta revisa 2 elevado a N menos 1 combinaciones. Con 20 partidos ya son 1.048.575.' },
|
|
45
|
+
], bibliography, howTo: [
|
|
46
|
+
{ name: 'Introduce los partidos', text: 'Añade cada partido con sus escaños y un nombre distinto.' },
|
|
47
|
+
{ name: 'Fija el umbral', text: 'Indica cuántos escaños necesita una coalición para ganar.' },
|
|
48
|
+
{ name: 'Revisa los decisivos', text: 'Usa los anillos y los recuentos para identificar partidos necesarios.' },
|
|
49
|
+
{ name: 'Lee la tabla', text: 'Compara grupos mínimos, excedente y miembros redundantes.' },
|
|
50
|
+
{ name: 'Cambia un supuesto', text: 'Ajusta un escaño o el umbral cada vez.' },
|
|
51
|
+
], schemas: [softwareApplication, faqPage, howTo] as unknown as Record<string, unknown>[] };
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { FAQPage, HowTo, SoftwareApplication } from 'schema-dts';
|
|
2
|
+
import { bibliography } from '../bibliography';
|
|
3
|
+
import type { CoalitionMajorityLocaleContent } from '../entry';
|
|
4
|
+
import type { CoalitionMajorityUI } from '../ui';
|
|
5
|
+
|
|
6
|
+
const ui: CoalitionMajorityUI = {
|
|
7
|
+
addParty: `Ajouter un parti`, allCoalitions: `Toutes les coalitions gagnantes`, calculate: `Calculer les coalitions`, chamberSeats: `Sièges totaux`, coalitionSceneLabel: `Constellation des coalitions`, criticalDefections: `Défections critiques`, emptyState: `Ajoutez au moins deux partis et saisissez leurs sièges.`, explanation: `Le modèle suppose que chaque parti peut rejoindre toute combinaison. Il mesure l'arithmétique des sièges, pas la compatibilité politique.`, edgeCasesText: `Les noms vides ou doublons sont rendus uniques. Un parti sans siège ne peut jamais être décisif. Au-delà de 20 partis, la recherche exacte s'arrête.`, edgeCasesTitle: `Cas limites et avertissements`, filterCoalitions: `Afficher`, maximumParties: `Vingt partis maximum, car l'énumération exhaustive croît comme 2 puissance N.`, majorityThreshold: `Seuil de majorité`, methodTitle: `Modèle appliqué`, methodText: `Chaque sous-ensemble non vide est vérifié. Une coalition gagne lorsqu'elle atteint le seuil déclaré. Un parti est décisif si son départ fait perdre une coalition gagnante.`, minimalCoalition: `Coalition gagnante minimale`, noMatchingCoalitions: `Aucune coalition ne correspond à ce filtre.`, noPivots: `Aucun parti n'est décisif avec ce seuil.`, none: `Aucun`, notDoText: `L'outil n'évalue ni l'idéologie, ni la compatibilité politique, ni la négociation, ni la légitimité, ni la stabilité, ni la probabilité de former un gouvernement.`, notDoTitle: `Ce que cet outil ne fait pas`, partyName: `Nom du parti`, partySeats: `Sièges`, partyTable: `Détails de la coalition`, pivotal: `décisif`, pivotalParty: `Parti décisif`, redundantMembers: `Membres redondants`, removeParty: `Supprimer le parti`, reset: `Réinitialiser l'exemple`, resultTitle: `Carte des coalitions`, seats: `sièges`, surplus: `excédent`, surplusCoalition: `Coalition excédentaire`, threshold: `Seuil`, thresholdHint: `Une coalition gagne lorsque ses sièges atteignent ou dépassent ce seuil.`, totalCoalitions: `Coalitions non vides possibles`, winningCoalitions: `Coalitions gagnantes`,
|
|
8
|
+
};
|
|
9
|
+
const softwareApplication: SoftwareApplication = { '@type': `SoftwareApplication`, name: `Calculateur de majorité des coalitions`, applicationCategory: `EducationalApplication`, operatingSystem: `Any`, description: `Énumérer les coalitions pondérées par les sièges, les groupes gagnants minimaux et les partis décisifs sous un seuil déclaré.`, url: `https://gamebob.dev/fr/calculateur-majorite-coalitions`, offers: { '@type': `Offer`, price: `0`, priceCurrency: `EUR` } };
|
|
10
|
+
const faqPage: FAQPage = { '@type': `FAQPage`, mainEntity: [{ '@type': `Question`, name: `Quand une coalition est-elle minimale ?`, acceptedAnswer: { '@type': `Answer`, text: `Elle est minimale lorsque le départ de n'importe quel membre la fait passer sous le seuil de sièges déclaré. Une coalition excédentaire reste gagnante après le départ d'au moins un membre.` } }, { '@type': `Question`, name: `Que signifie décisif ?`, acceptedAnswer: { '@type': `Answer`, text: `Un parti est compté comme décisif chaque fois que son départ transforme une coalition gagnante en coalition perdante. Sa part est divisée par le total des défections critiques.` } }, { '@type': `Question`, name: `Le calculateur prédit-il un gouvernement réel ?`, acceptedAnswer: { '@type': `Answer`, text: `Non. Il évalue uniquement les poids de sièges et le seuil déclarés. L'idéologie, la négociation, le soutien minoritaire et les règles constitutionnelles sont exclus.` } }, { '@type': `Question`, name: `Pourquoi la limite est-elle de 20 partis ?`, acceptedAnswer: { '@type': `Answer`, text: `L'outil vérifie chaque sous-ensemble non vide. Vingt partis produisent déjà 1 048 575 combinaisons, une limite pratique dans un navigateur.` } }] };
|
|
11
|
+
const howTo: HowTo = { '@type': `HowTo`, name: `Explorer une majorité pondérée`, step: [{ '@type': `HowToStep`, name: `Saisir les partis`, text: `Ajoutez chaque parti et son nombre de sièges. Des noms distincts rendent le résultat vérifiable.` }, { '@type': `HowToStep`, name: `Définir le seuil`, text: `Saisissez le nombre de sièges qu'une coalition doit atteindre.` }, { '@type': `HowToStep`, name: `Inspecter la constellation`, text: `Repérez les anneaux accentués et comparez les comptes décisifs aux sièges.` }, { '@type': `HowToStep`, name: `Lire le tableau`, text: `Comparez coalitions minimales, excédent et membres redondants.` }, { '@type': `HowToStep`, name: `Tester un autre scénario`, text: `Modifiez un nombre de sièges ou le seuil à la fois.` }] };
|
|
12
|
+
export const content: CoalitionMajorityLocaleContent = { slug: `calculateur-majorite-coalitions`, title: `Calculateur de majorité des coalitions`, description: `Explorez chaque coalition fondée sur les sièges sous un seuil de majorité déclaré. Trouvez les groupes minimaux, l'excédent et les partis décisifs.`, ui, seo: [{ type: `title`, text: `Quelles combinaisons de sièges atteignent la majorité`, level: 2 }, { type: `paragraph`, html: `Une coalition est gagnante lorsque les sièges de ses membres atteignent ou dépassent le seuil que vous déclarez. Ce calculateur énumère chaque combinaison non vide et indique l'excédent exact. Vous pouvez ainsi vérifier pourquoi un groupe gagne.` }, { type: `title`, text: `Coalitions minimales et coalitions excédentaires`, level: 2 }, { type: `paragraph`, html: `Une coalition gagnante minimale perd sa majorité dès qu'un membre part. Une coalition excédentaire reste au-dessus du seuil après le départ d'un ou plusieurs membres. Cette distinction porte sur les sièges, pas sur la stabilité politique.` }, { type: `table`, headers: [`Résultat`, `Test appliqué`, `Ce que cela montre`], rows: [[`Coalition gagnante`, `Les sièges atteignent le seuil.`, `Quels groupes peuvent gagner en principe.`], [`Coalition gagnante minimale`, `Chaque membre est nécessaire au gain.`, `Quels groupes n'ont aucun excédent arithmétique.`], [`Coalition excédentaire`, `Au moins un membre peut partir et le groupe gagne encore.`, `Où une majorité possède des partenaires redondants.`]] }, { type: `title`, text: `Lire les partis décisifs comme une arithmétique`, level: 2 }, { type: `paragraph`, html: `Le compte décisif utilise un test de défection critique de type Banzhaf. Un parti compte lorsque son départ transforme une coalition gagnante en coalition perdante. La part est normalisée sur toutes les défections critiques du scénario.` }, { type: `list`, items: [`Commencez par le total des sièges et appliquez le seuil de votre règle.`, `Gardez le même seuil pour comparer deux répartitions.`, `Consultez les membres redondants avant d'interpréter la liste des partis décisifs.`, `Modifiez un seul parti à la fois pour comprendre la sensibilité.`, `Considérez les noms doublons et les partis sans siège comme des problèmes de données.`] }, { type: `title`, text: `Une recherche exacte encadrée`, level: 2 }, { type: `paragraph`, html: `Pour N partis, le calculateur évalue 2 puissance N moins 1 coalitions non vides. La limite de 20 garde la recherche exhaustive lisible et utilisable dans le navigateur. Le tableau présente un sous-ensemble lisible, tandis que les totaux couvrent toute la recherche.` }, { type: `tip`, title: `Les seuils sont des hypothèses, pas des lois`, html: `Le calculateur ne reconnaît ni règle constitutionnelle, ni investiture, ni majorité parlementaire valide. Saisissez le seuil vous-même et conservez cette règle avec le résultat.` }, { type: `title`, text: `Ce que le modèle ne peut pas déduire`, level: 2 }, { type: `paragraph`, html: `Les sièges décrivent la capacité à franchir un nombre, pas la volonté de coopérer. Le modèle n'évalue ni idéologie, ni distance politique, ni négociation, ni légitimité, ni stabilité, ni soutien extérieur.` }, { type: `tip`, title: `Utilisez le résultat comme piste d'audit`, html: `Lorsqu'un parti est décisif, trouvez une coalition gagnante où son départ franchit le seuil à la baisse. Pour une coalition excédentaire, examinez ses membres redondants.` }], faq: [{ question: `Quand une coalition est-elle minimale ?`, answer: `Lorsque le départ de chacun de ses membres la fait perdre. Si au moins un membre peut partir tout en conservant le seuil, la coalition est excédentaire.` }, { question: `Que signifie décisif ?`, answer: `Un parti est décisif dans chaque coalition gagnante qui devient perdante sans lui. Sa part est son compte critique divisé par le total critique.` }, { question: `Cela prédit-il un gouvernement réel ?`, answer: `Non. Seule l'arithmétique des sièges est évaluée. La compatibilité politique, la négociation et les règles constitutionnelles sont exclues.` }, { question: `Pourquoi vingt partis ?`, answer: `La recherche exacte vérifie 2 puissance N moins 1 combinaisons. Avec vingt partis, il y en a déjà 1 048 575.` }], bibliography, howTo: [{ name: `Saisir les partis`, text: `Ajoutez chaque parti avec ses sièges et un nom distinct.` }, { name: `Définir le seuil`, text: `Indiquez le nombre de sièges nécessaire pour gagner.` }, { name: `Examiner les partis décisifs`, text: `Utilisez les anneaux et les comptes pour repérer les partis nécessaires.` }, { name: `Lire le tableau`, text: `Comparez les groupes minimaux, l'excédent et les membres redondants.` }, { name: `Modifier une hypothèse`, text: `Ajustez un siège ou le seuil à la fois.` }], schemas: [softwareApplication, faqPage, howTo] as unknown as Record<string, unknown>[] };
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import type { FAQPage, HowTo, SoftwareApplication } from 'schema-dts';
|
|
2
|
+
import { bibliography } from '../bibliography';
|
|
3
|
+
import type { CoalitionMajorityLocaleContent } from '../entry';
|
|
4
|
+
import type { CoalitionMajorityUI } from '../ui';
|
|
5
|
+
|
|
6
|
+
const ui: CoalitionMajorityUI = {
|
|
7
|
+
addParty: 'Tambah partai', allCoalitions: 'Semua koalisi pemenang', calculate: 'Hitung koalisi', chamberSeats: 'Total kursi', coalitionSceneLabel: 'Konstelasi koalisi', criticalDefections: 'Pengunduran diri kritis', emptyState: 'Tambahkan setidaknya dua partai dan masukkan jumlah kursinya.', explanation: 'Model ini menganggap setiap partai dapat bergabung dengan kombinasi apa pun. Yang diukur adalah aritmetika kursi, bukan kecocokan politik.', edgeCasesText: 'Nama kosong atau ganda dibuat unik. Partai dengan nol kursi tidak pernah bisa menjadi penentu. Pencarian eksak berhenti di atas 20 partai.', edgeCasesTitle: 'Kasus batas dan peringatan', filterCoalitions: 'Tampilkan', maximumParties: 'Maksimal 20 partai karena enumerasi lengkap tumbuh sebagai 2 pangkat N.', majorityThreshold: 'Ambang mayoritas', methodTitle: 'Model yang digunakan', methodText: 'Setiap subset tidak kosong diperiksa. Koalisi menang jika mencapai ambang yang ditetapkan. Partai menjadi penentu jika pengunduran dirinya membuat koalisi pemenang kalah.', minimalCoalition: 'Koalisi pemenang minimal', noMatchingCoalitions: 'Tidak ada koalisi yang cocok dengan filter ini.', noPivots: 'Tidak ada partai yang menjadi penentu pada ambang ini.', none: 'Tidak ada', notDoText: 'Alat ini tidak menilai ideologi, kecocokan kebijakan, negosiasi, legitimasi, stabilitas, atau kemungkinan terbentuknya pemerintahan.', notDoTitle: 'Yang tidak dilakukan alat ini', partyName: 'Nama partai', partySeats: 'Kursi', partyTable: 'Detail koalisi', pivotal: 'penentu', pivotalParty: 'Partai penentu', redundantMembers: 'Anggota redundan', removeParty: 'Hapus partai', reset: 'Pulihkan contoh', resultTitle: 'Peta koalisi', seats: 'kursi', surplus: 'surplus', surplusCoalition: 'Koalisi surplus', threshold: 'Ambang', thresholdHint: 'Koalisi menang ketika jumlah kursinya mencapai atau melampaui ambang ini.', totalCoalitions: 'Koalisi tidak kosong yang mungkin', winningCoalitions: 'Koalisi pemenang',
|
|
8
|
+
};
|
|
9
|
+
const softwareApplication: SoftwareApplication = { '@type': 'SoftwareApplication', name: 'Kalkulator Mayoritas Koalisi', applicationCategory: 'EducationalApplication', operatingSystem: 'Any', description: 'Enumerasi koalisi berbobot kursi, kelompok pemenang minimal, dan partai penentu di bawah ambang yang ditetapkan.', url: 'https://gamebob.dev/id/kalkulator-mayoritas-koalisi', offers: { '@type': 'Offer', price: '0', priceCurrency: 'USD' } };
|
|
10
|
+
const faqPage: FAQPage = { '@type': 'FAQPage', mainEntity: [
|
|
11
|
+
{ '@type': 'Question', name: 'Kapan koalisi disebut minimal?', acceptedAnswer: { '@type': 'Answer', text: 'Koalisi minimal jika keluarnya anggota mana pun membuat jumlah kursinya turun di bawah ambang yang ditetapkan. Koalisi surplus tetap menang setelah setidaknya satu anggota keluar.' } },
|
|
12
|
+
{ '@type': 'Question', name: 'Apa arti penentu?', acceptedAnswer: { '@type': 'Answer', text: 'Partai dihitung sebagai penentu setiap kali pengunduran dirinya mengubah koalisi pemenang menjadi koalisi kalah. Bagiannya dibagi dengan seluruh pengunduran diri kritis.' } },
|
|
13
|
+
{ '@type': 'Question', name: 'Apakah kalkulator ini memprediksi pemerintahan nyata?', acceptedAnswer: { '@type': 'Answer', text: 'Tidak. Kalkulator hanya menilai bobot kursi dan ambang yang dimasukkan. Ideologi, negosiasi, dukungan minoritas, dan aturan konstitusional berada di luar model.' } },
|
|
14
|
+
{ '@type': 'Question', name: 'Mengapa ada batas 20 partai?', acceptedAnswer: { '@type': 'Answer', text: 'Alat ini memeriksa setiap subset tidak kosong. Dua puluh partai sudah menghasilkan 1.048.575 kombinasi, batas yang praktis untuk browser.' } },
|
|
15
|
+
] };
|
|
16
|
+
const howTo: HowTo = { '@type': 'HowTo', name: 'Menjelajahi mayoritas berbobot', step: [
|
|
17
|
+
{ '@type': 'HowToStep', name: 'Masukkan partai', text: 'Tambahkan setiap partai dan masukkan bobot kursinya. Gunakan nama yang berbeda agar hasil mudah diaudit.' },
|
|
18
|
+
{ '@type': 'HowToStep', name: 'Atur ambang', text: 'Masukkan jumlah kursi yang harus dicapai koalisi untuk menang.' },
|
|
19
|
+
{ '@type': 'HowToStep', name: 'Periksa konstelasi', text: 'Cari cincin aksen dan bandingkan hitungan penentu dengan jumlah kursi.' },
|
|
20
|
+
{ '@type': 'HowToStep', name: 'Baca tabel koalisi', text: 'Bandingkan koalisi minimal, surplus kursi, dan anggota redundan.' },
|
|
21
|
+
{ '@type': 'HowToStep', name: 'Uji skenario lain', text: 'Ubah satu jumlah kursi atau ambang setiap kali agar perubahan mudah dipahami.' },
|
|
22
|
+
] };
|
|
23
|
+
export const content: CoalitionMajorityLocaleContent = { slug: 'kalkulator-mayoritas-koalisi', title: 'Kalkulator Mayoritas Koalisi', description: 'Jelajahi setiap koalisi berbasis kursi di bawah ambang mayoritas yang ditetapkan, termasuk kelompok minimal, surplus, dan partai penentu.', ui, seo: [
|
|
24
|
+
{ type: 'title', text: 'Kombinasi Kursi yang Dapat Mencapai Mayoritas', level: 2 },
|
|
25
|
+
{ type: 'paragraph', html: 'Koalisi menang ketika kursi para anggotanya mencapai atau melampaui ambang yang Anda tetapkan. Kalkulator ini mencantumkan setiap kombinasi tidak kosong dan menunjukkan surplus tepat di atas ambang, sehingga alasan kemenangan dapat diperiksa.' },
|
|
26
|
+
{ type: 'title', text: 'Koalisi Pemenang Minimal dan Surplus', level: 2 },
|
|
27
|
+
{ type: 'paragraph', html: 'Koalisi pemenang minimal kehilangan mayoritas ketika satu anggota mana pun keluar. Koalisi surplus tetap berada di atas ambang setelah satu atau lebih anggota keluar. Ini adalah perbedaan aritmetika kursi, bukan ramalan stabilitas politik.' },
|
|
28
|
+
{ type: 'table', headers: ['Hasil', 'Tes', 'Yang terlihat'], rows: [['Koalisi pemenang', 'Kursi anggota setidaknya sama dengan ambang.', 'Kelompok yang secara prinsip memiliki cukup kursi.'], ['Koalisi pemenang minimal', 'Setiap anggota penting untuk tetap menang.', 'Kelompok tanpa surplus aritmetika kursi.'], ['Koalisi surplus', 'Satu anggota dapat keluar dan kelompok tetap menang.', 'Mayoritas dengan mitra redundan.']] },
|
|
29
|
+
{ type: 'title', text: 'Membaca Partai Penentu sebagai Aritmetika Kursi', level: 2 },
|
|
30
|
+
{ type: 'paragraph', html: 'Hitungan penentu memakai uji pengunduran diri kritis bergaya Banzhaf. Partai dihitung ketika keluarnya mengubah koalisi pemenang menjadi kalah. Bagiannya dinormalisasi terhadap semua pengunduran diri kritis dalam skenario.' },
|
|
31
|
+
{ type: 'list', items: ['Mulai dari total kursi dan gunakan ambang yang sesuai dengan aturan Anda.', 'Pertahankan ambang yang sama saat membandingkan dua pembagian kursi.', 'Periksa anggota redundan di tabel sebelum menafsirkan daftar partai penentu.', 'Ubah satu partai setiap kali untuk melihat sensitivitas.', 'Anggap nama ganda dan partai tanpa kursi sebagai masalah data.'] },
|
|
32
|
+
{ type: 'title', text: 'Pencarian Eksak yang Terbatas', level: 2 },
|
|
33
|
+
{ type: 'paragraph', html: 'Untuk N partai, kalkulator mengevaluasi 2 pangkat N dikurangi 1 koalisi tidak kosong. Batas 20 partai menjaga pencarian lengkap tetap transparan dan nyaman di browser. Tabel menampilkan bagian yang mudah dibaca, sedangkan ringkasan mencakup seluruh pencarian.' },
|
|
34
|
+
{ type: 'tip', title: 'Ambang adalah asumsi, bukan hukum', html: 'Kalkulator tidak mengenali aturan negara, pelantikan, perjanjian kepercayaan, atau mayoritas parlemen yang sah. Masukkan ambang sendiri dan simpan aturannya bersama hasil.' },
|
|
35
|
+
{ type: 'title', text: 'Yang Tidak Dapat Disimpulkan Model', level: 2 },
|
|
36
|
+
{ type: 'paragraph', html: 'Bobot kursi menunjukkan kemampuan melewati angka, bukan kemungkinan bekerja sama. Model tidak menilai ideologi, jarak kebijakan, negosiasi, legitimasi, stabilitas, faksi internal, atau dukungan dari luar.' },
|
|
37
|
+
{ type: 'tip', title: 'Gunakan hasil sebagai jejak audit', html: 'Jika sebuah partai ditandai sebagai penentu, temukan koalisi pemenang yang turun melewati ambang ketika partai itu keluar. Untuk koalisi surplus, periksa anggota redundannya.' },
|
|
38
|
+
], faq: [
|
|
39
|
+
{ question: 'Kapan koalisi disebut minimal?', answer: 'Ketika keluarnya anggota mana pun membuat koalisi kalah. Jika satu anggota dapat keluar dan ambang tetap tercapai, koalisi itu memiliki surplus.' },
|
|
40
|
+
{ question: 'Apa arti penentu?', answer: 'Partai penentu ada di setiap koalisi pemenang yang menjadi kalah tanpa dirinya. Bagiannya adalah hitungan kritis dibagi total kritis.' },
|
|
41
|
+
{ question: 'Apakah ini memprediksi pemerintahan nyata?', answer: 'Tidak. Ini hanya mengevaluasi aritmetika kursi. Kecocokan politik, negosiasi, dan aturan konstitusional berada di luar model.' },
|
|
42
|
+
{ question: 'Mengapa batasnya 20 partai?', answer: 'Pencarian eksak memeriksa 2 pangkat N dikurangi 1 kombinasi. Dua puluh partai menghasilkan 1.048.575 kombinasi.' },
|
|
43
|
+
], bibliography, howTo: [
|
|
44
|
+
{ name: 'Masukkan partai', text: 'Tambahkan partai dengan kursi dan nama yang berbeda.' }, { name: 'Atur ambang', text: 'Masukkan jumlah kursi yang diperlukan untuk menang.' }, { name: 'Periksa partai penentu', text: 'Gunakan cincin dan hitungan untuk menemukan partai yang diperlukan.' }, { name: 'Baca tabel', text: 'Bandingkan kelompok minimal, surplus, dan anggota redundan.' }, { name: 'Ubah satu asumsi', text: 'Sesuaikan satu jumlah kursi atau ambang setiap kali.' },
|
|
45
|
+
], schemas: [softwareApplication, faqPage, howTo] as unknown as Record<string, unknown>[] };
|